diff --git a/.github/workflows/BuildDocDryRun.yml b/.github/workflows/BuildDocDryRun.yml new file mode 100644 index 0000000..43b09ae --- /dev/null +++ b/.github/workflows/BuildDocDryRun.yml @@ -0,0 +1,17 @@ +name: DocumentationDryRun + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: '1.6' + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + run: julia --project=docs/ docs/make.jl + diff --git a/.gitignore b/.gitignore index 91451c1..b3aa07f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ *.log Manifest.toml docs/build/ + +*.vscode/ +*.DS_Store \ No newline at end of file diff --git a/Project.toml b/Project.toml index 6b285df..6d45470 100644 --- a/Project.toml +++ b/Project.toml @@ -11,11 +11,17 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -Parameters = "0.10,0.11,0.12" DSP = "0.7" FFTW = "1.4.5" SpecialFunctions = "2.0" julia = "1.6" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" + + +[targets] +test = ["Test", "Documenter"] diff --git a/docs/Project.toml b/docs/Project.toml index dfa65cd..c3a8da9 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,2 +1,7 @@ [deps] +DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" +SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" diff --git a/docs/make.jl b/docs/make.jl index 2d41472..b2d73de 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,20 +1,24 @@ push!(LOAD_PATH, "../src/") + using Documenter, DigitalComm -makedocs(sitename="DigitalComm.jl", - format = Documenter.HTML(), - pages = Any[ - "Introduction to DigitalComm" => "index.md", - "Function list" => "base.md", - "Examples" => Any[ - "Examples/example_AWGN.md", - "Examples/example_BER.md", - "Examples/example_PSD.md", - ], - ], - ); +DocMeta.setdocmeta!(DigitalComm, :DocTestSetup, :(using DigitalComm); recursive=true) -#makedocs(sitename="My Documentation", format = Documenter.HTML(prettyurls = false)) +makedocs( + modules = [DigitalComm], + sitename="DigitalComm.jl", + format = Documenter.HTML(), + pages = Any[ + "Introduction to DigitalComm" => "index.md", + "Function list" => "base.md", + "Examples" => Any[ + "Examples/example_AWGN.md", + "Examples/example_BER.md", + "Examples/example_PSD.md", + ], + ], + doctest = true, +); deploydocs( repo = "github.com/JuliaTelecom/DigitalComm.jl", diff --git a/docs/src/Examples/example_AWGN.md b/docs/src/Examples/example_AWGN.md index 9154783..2c88835 100644 --- a/docs/src/Examples/example_AWGN.md +++ b/docs/src/Examples/example_AWGN.md @@ -1,44 +1,47 @@ -# Transmission of xQAM with additive white Gaussian noise +# Transmission of xQAM with additive white Gaussian noise To simulate a transmission of QPSK // 16QAM // 64QAM // 256QAM other a white additive Gaussian noise and display the received constellation, -the following code can be used - - # ---------------------------------------------------- - # --- Transmitter - # ---------------------------------------------------- - using DigitalComm - using Plots - # --- Parameters - snr = 20; - mcs = 16; - nbBits = 1024* Int(log2(mcs)); - # --- Binary sequence generation - bitSeq = genBitSequence(nbBits); - # --- QPSK mapping - qamSeq = bitMappingQAM(mcs,bitSeq); - # ---------------------------------------------------- - # --- Channel - # ---------------------------------------------------- - # --- AWGN - # Theoretical power is 1 (normalized constellation) - qamNoise, = addNoise(qamSeq,snr,1); - # ---------------------------------------------------- - # --- Rx Stage: SRRC - # ---------------------------------------------------- - # --- Binary demapper - bitDec = bitDemappingQAM(mcs,qamNoise); - # --- BER measure - ber = sum(xor.(bitDec,bitSeq)) /length(bitSeq); - # --- Display constellation - plt = scatter(real(qamNoise),imag(qamNoise),label="Noisy"); - scatter!(plt,real(qamSeq),imag(qamSeq),label="Ideal"); - xlabel!("Real part"); - ylabel!("Imag part"); - display(plt); - -It plots the received constellation impaired by noise (here a 20dB SNR is used) -![Constellation](./../img/constellation.png) +the following code can be used +## Transmitter +```@example AWG +using ..DigitalComm # hide +using Plots +# --- Parameters +snr = 20; +mcs = 16; +nbBits = 1024* Int(log2(mcs)); +# --- Binary sequence generation +bitSeq = genBitSequence(nbBits); +# --- QPSK mapping +qamSeq = bitMappingQAM(mcs,bitSeq); nothing +``` +## Channel + +```@example AWG +# --- AWGN +# Theoretical power is 1 (normalized constellation) +qamNoise, = addNoise(qamSeq,snr,1); nothing +``` + +## Recevicer + +```@example AWG +# --- Binary demapper +bitDec = bitDemappingQAM(mcs,qamNoise); +# --- BER measure +ber = sum(xor.(bitDec,bitSeq)) /length(bitSeq); +# --- Display constellation +plt = scatter(real(qamNoise),imag(qamNoise),label="Noisy"); +scatter(plt,real(qamSeq),imag(qamSeq),label="Ideal"); +xlabel!("Real part"); +ylabel!("Imag part"); + +savefig("constellation.svg"); nothing # hide +``` + +It plots the received constellation impaired by noise (here a 20dB SNR is used) +![Constellation](constellation.svg) diff --git a/docs/src/Examples/example_BER.md b/docs/src/Examples/example_BER.md index bd9a89f..4c2d118 100644 --- a/docs/src/Examples/example_BER.md +++ b/docs/src/Examples/example_BER.md @@ -1,121 +1,109 @@ -# Compute the theoretical BER for AWGN channel and various constellation size +# Compute the theoretical BER for AWGN channel and various constellation size Based on the previous skeleton, we can now compute an iterative testbench to compute the Bit Error Rate for various constellation size, and compare the simulation with the theory. -As a gentle reminder, the theoretical bit error rate can be approximated as +As a gentle reminder, the theoretical bit error rate can be approximated as ``\mathrm{BER} = \frac{ 4 \left( 1 - \frac{1}{\sqrt{M}} \right) }{ \log_2(M)} \times Q( \sqrt{ \frac{6 \log_2(M)}{2(M-1)} \frac{Eb}{N_0}}`` - -First of all let's call the modules - - - using DigitalComm - using PGFPlotsX - - -We define first the main monte-carlo function that compute an elementary Tx-Rx link, and returns the number of error and number of bit computed (to be accumulated) - - - function monteCarlo(snr,mcs,nbSymb) - # Number of bits - nbBits = nbSymb * Int(log2(mcs)); - # --- Binary sequence generation - bitSeq = genBitSequence(nbBits); - # --- QPSK mapping - qamSeq = bitMappingQAM(mcs,bitSeq); - # ---------------------------------------------------- - # --- Channel - # ---------------------------------------------------- - # --- AWGN - # Theoretical power is 1 (normalized constellation) - qamNoise, = addNoise(qamSeq,snr,1); - # ---------------------------------------------------- - # --- Rx Stage: SRRC - # ---------------------------------------------------- - # --- Binary demapper - bitDec = bitDemappingQAM(mcs,qamNoise); - # --- Error counter - nbE = sum(xor.(bitDec,bitSeq)); - # --- Return Error and bits - return (nbE,nbBits); - end - - -A function to plot the BER versus the SNR, for different mcs and compare to theory - - - function doPlot(snrVect,ber,qamVect) - a = 0; - @pgf a = Axis({ - ymode = "log", - height ="3in", - width ="4in", - grid, - xlabel = "SNR [dB]", - ylabel = "Bit Error Rate ", - ymax = 1, - ymin = 10.0^(-5), - title = "AWGN BER for QAM", - legend_style="{at={(0,0)},anchor=south west,legend cell align=left,align=left,draw=white!15!black}" - }, - Plot({color="red",mark="square*"},Table([snrVect,ber[1,:]])), - LegendEntry("QPSK"), - Plot({color="green",mark="*"},Table([snrVect,ber[2,:]])), - LegendEntry("16-QAM"), - - Plot({color="purple",mark="triangle*"},Table([snrVect,ber[3,:]])), - LegendEntry("64-QAM"), - Plot({color="blue",mark="diamond*"},Table([snrVect,ber[4,:]])), - LegendEntry("256-QAM"), - ); - # --- Adding theoretical curve - snrLin = (10.0).^(snrVect/10) - for qamScheme = qamVect - ebNo = snrLin / log2(qamScheme); - # This approximation is only valid for high SNR (one symbol error is converted to one bit error with Gray coding). - berTheo = 4 * ( 1 - 1 / sqrt(qamScheme)) / log2(qamScheme) * qFunc.(sqrt.( 2*ebNo * 3 * log2(qamScheme) / (2*(qamScheme-1) ))); - @pgf push!(a,Plot({color="black"},Table([snrVect,berTheo]))); - end - display(a); - end - - -Then, the main routine to compute the BER for a given number of iterations and a range of SNR - - - function main() - # --- Parameters - nbIt = 10000; # Number of iterations - nbSymb = 1024; # Number of symbols per iterations - mcs = [4,16,64,256]; # Constellation size - snrRange = (-1:26); # SNR, expressed in dB - # --- Init performance metrics - nbSNR = length(snrRange); - ber = zeros(Float64,length(mcs),nbSNR); - for iMcs = 1 : 1 : length(mcs) - for iSNR = 1 : 1 : nbSNR - # --- Create BER counters - nbE = 0; - nbB = 0; - for iN = 1 : 1 : nbIt - # --- Elementary MC call - # Corresponds to a given SNR and a given iteration - # As we are ergodic in AWGN, it is only nbSymb*nbIt that matters for BER computation - (a,b) = monteCarlo(snrRange[iSNR],mcs[iMcs],nbSymb); - # --- Update counters - nbE += a; # Increment errors - nbB += b; # Increment bit counters - end - ber[iMcs,iSNR] = nbE / nbB; - end - end - # --- Plotting routine - doPlot(snrRange,ber,mcs); - end - +First of all let's call the modules + + using DigitalComm + using PGFPlotsX + +We define first the main monte-carlo function that compute an elementary Tx-Rx link, and returns the number of error and number of bit computed (to be accumulated) + + function monteCarlo(snr,mcs,nbSymb) + # Number of bits + nbBits = nbSymb * Int(log2(mcs)); + # --- Binary sequence generation + bitSeq = genBitSequence(nbBits); + # --- QPSK mapping + qamSeq = bitMappingQAM(mcs,bitSeq); + # ---------------------------------------------------- + # --- Channel + # ---------------------------------------------------- + # --- AWGN + # Theoretical power is 1 (normalized constellation) + qamNoise, = addNoise(qamSeq,snr,1); + # ---------------------------------------------------- + # --- Rx Stage: SRRC + # ---------------------------------------------------- + # --- Binary demapper + bitDec = bitDemappingQAM(mcs,qamNoise); + # --- Error counter + nbE = sum(xor.(bitDec,bitSeq)); + # --- Return Error and bits + return (nbE,nbBits); + end + +A function to plot the BER versus the SNR, for different mcs and compare to theory + + function doPlot(snrVect,ber,qamVect) + a = 0; + @pgf a = Axis({ + ymode = "log", + height ="3in", + width ="4in", + grid, + xlabel = "SNR [dB]", + ylabel = "Bit Error Rate ", + ymax = 1, + ymin = 10.0^(-5), + title = "AWGN BER for QAM", + legend_style="{at={(0,0)},anchor=south west,legend cell align=left,align=left,draw=white!15!black}" + }, + Plot({color="red",mark="square*"},Table([snrVect,ber[1,:]])), + LegendEntry("QPSK"), + Plot({color="green",mark="*"},Table([snrVect,ber[2,:]])), + LegendEntry("16-QAM"), + + Plot({color="purple",mark="triangle*"},Table([snrVect,ber[3,:]])), + LegendEntry("64-QAM"), + Plot({color="blue",mark="diamond*"},Table([snrVect,ber[4,:]])), + LegendEntry("256-QAM"), + ); + # --- Adding theoretical curve + snrLin = (10.0).^(snrVect/10) + for qamScheme = qamVect + ebNo = snrLin / log2(qamScheme); + # This approximation is only valid for high SNR (one symbol error is converted to one bit error with Gray coding). + berTheo = 4 * ( 1 - 1 / sqrt(qamScheme)) / log2(qamScheme) * qFunc.(sqrt.( 2*ebNo * 3 * log2(qamScheme) / (2*(qamScheme-1) ))); + @pgf push!(a,Plot({color="black"},Table([snrVect,berTheo]))); + end + display(a); + end + +Then, the main routine to compute the BER for a given number of iterations and a range of SNR + + function main() + # --- Parameters + nbIt = 10000; # Number of iterations + nbSymb = 1024; # Number of symbols per iterations + mcs = [4,16,64,256]; # Constellation size + snrRange = (-1:26); # SNR, expressed in dB + # --- Init performance metrics + nbSNR = length(snrRange); + ber = zeros(Float64,length(mcs),nbSNR); + for iMcs = 1 : 1 : length(mcs) + for iSNR = 1 : 1 : nbSNR + # --- Create BER counters + nbE = 0; + nbB = 0; + for iN = 1 : 1 : nbIt + # --- Elementary MC call + # Corresponds to a given SNR and a given iteration + # As we are ergodic in AWGN, it is only nbSymb*nbIt that matters for BER computation + (a,b) = monteCarlo(snrRange[iSNR],mcs[iMcs],nbSymb); + # --- Update counters + nbE += a; # Increment errors + nbB += b; # Increment bit counters + end + ber[iMcs,iSNR] = nbE / nbB; + end + end + # --- Plotting routine + doPlot(snrRange,ber,mcs); + end The output plot is the following, showing adequacy between theory and practise for high SNR (the theoretical curve is under the assumption that one symbol error leads to one erroneous bit (gray coding) which is true only with intermediate noise levels). ![BER](./../img/BER_AWGN.png) - - - diff --git a/docs/src/Examples/example_PSD.md b/docs/src/Examples/example_PSD.md index 8dd1f8f..1bb064f 100644 --- a/docs/src/Examples/example_PSD.md +++ b/docs/src/Examples/example_PSD.md @@ -1,184 +1,181 @@ -# Plotting PSD of several multicarrier waveforms +# Plotting PSD of several multicarrier waveforms -The purpose is to compre the Power spectral density of several multicarrier waveform. The following module can be used: +The purpose is to compre the Power spectral density of several multicarrier waveform. The following module can be used: - module example_PSD_waveform - # ---------------------------------------------------- - # --- Modules - # ---------------------------------------------------- - using DigitalComm - # --- External Modules - using Plots - gr(); - using Printf - using FFTW - # ---------------------------------------------------- - # --- Core functions - # ---------------------------------------------------- - """ psdWaveform.m - --- - Compute the power spectral density (i.e the spectrum here) of the signal parametrized by the waveform structure waveform, for a number of symbol nbSymb. - The frequency allocation is the one inherited from the waveform structure (i.e waveform.allocatedSubcarriers). - # --- Syntax - ( freq,psd ) = psdWaveform(waveform,nbSymb,allocatedSubcarriers); - # --- Input parameters - - waveform : Structure associated to transmitted waveform - - nbSymb : Number of symbol to be transmitted [Int] - - nbIt : Monte carlo parameter for PSD evaluation (should be > 1) - # --- Output parameters - - freq : Vector of frequency evaluation (between -0.5 and 0.5). [Array{Float64,L}] - - psd : Spectrum evaluated on freq [Array{Complex{Float64}},L] - # --- Input parameters - - - # --- Output parameters - - - # --- - # v 1.0 - Robin Gerzaguet. - """ - function psdWaveform(waveform,nbSymb,nbIt) - # ---------------------------------------------------- - # --- PSD calculation - # ---------------------------------------------------- - # --- Getting frequency allocation - allocatedSubcarriers = waveform.allocatedSubcarriers; - # --- Getting number of bits - # First, frequency size - nbSubcarriers = length(allocatedSubcarriers); - # Force a fiven mcs - mcs = 4; # QPSK. - # Deduce number of required bits - nbBits = nbSymb * nbSubcarriers * Int(log2(mcs)); - # --- Init psd evaluator - psd = 0; - # --- Iterative PSD calculation - for iN = 1 : 1 : nbIt - # --- Binary sequence - bitSeq = genBitSequence(nbBits); - # Mapping - qamSeq = bitMappingQAM(mcs,bitSeq); - # --- T/F matrix - qamMat = reshape(qamSeq,nbSubcarriers,nbSymb); - # --- Signal - sigPSD = genSig(qamMat,waveform); - # --- Mean PSD: - psd = psd .+ 1/nbIt*1/length(sigPSD)*abs.(fftshift(fft(sigPSD))).^2; - end - # --- Calculating sampling frequency - # Returns Nyquist frequency - fe = 1; - Basefe = (0:(length(psd) .-1))./length(psd)*fe .-fe/2; - return (Basefe,psd); - end - # ---------------------------------------------------- - # --- Main routine - # ---------------------------------------------------- - function main() - # ---------------------------------------------------- - # --- Overall parameters - # ---------------------------------------------------- - # --- Overall PHY parameters - nbIt = 50; # --- Iteration number - nbSymb = 14; # --- Number of symbols (one frame) - nFFT = 1024; # --- Base FFT size - samplingFreq = 15.36; # --- Frequency value (MHz) - # --- Frequency allocation - #allocatedSubcarriers= getLTEAlloc(nFFT); - #allocatedSubcarriers = (1:12*4); - # 4 RB alloc. 1 RB space. 4 RB allocated - allocatedSubcarriers = [1:12*4; 12*5 .+ (1:12*4)]; - # ---------------------------------------------------- - # --- Waveform contender - # ---------------------------------------------------- - # --- Init OFDM structure - ofdm = initOFDM( - nFFT, # --- nFFT : FFT size - 72, # --- nCP : CP size - allocatedSubcarriers # --- allocatedSubcarriers : Subcarrier allocation - ); - # --- Init SCFDMA structure - scfdma = initSCFDMA( - nFFT, # --- nFFT : FFT size - 72, # --- nCP : CP size - allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation - 12; # --- sizeDFT : DFT preprocessing size - ); - # --- Init UF-OFDM structure - ufofdm = initUFOFDM( - nFFT, # --- nFFT : FFT size - 73, # --- L : Filter length (same size +1 due to conv) - allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation - applyPD=1, # --- applyPD : Do predistortion at Tx stage - attenuation=40, # --- attenuation : Filter attenuation in dB - ); - # --- Init BF-OFDM structure - bfofdm = initBFOFDM( - 32, # --- nFBMC : PPN size (max number of carriers) - 64, # --- nOFDM : Precoder size (OFDM sizer) - 3, # --- K : Overlapping factor - 9, # --- GI : CP size of precoder - 0.5, # --- δ : compression factor - allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation - "gaussian", # --- filterName : Pulse shape name - BT=0.36, # --- BT : Potential BT value for Gaussian - filterStopBand = 110, # --- filterStopBand : DC stopband value - fS=[], # --- fS : Potential frequency coefficient for FS filter - nFFT= 1024, # --- nFFT : associated FFT value in Rx - nCP= 72, # --- nCP : extended CP size - ); - # --- Init WOLA-OFDM structure - wola = initWOLA( - nFFT, # --- nFFT : FFT size - 72, # --- nCP : CP size - allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation - "triangle", # --- Window type @Tx side - 20, # --- Window size @Tx side - "triangle", # --- Window type @Rx side - 20, # --- Window size @Rx side - ); - fbmc = initFBMC( - nFFT, # --- nFFT : FFT size - 4, # --- K : Overlapping factor - allocatedSubcarriers # --- allocatedSubcarriers : Subcarrier allocation - ); - # ---------------------------------------------------- - # --- Merging structures - # ---------------------------------------------------- - # Create a dictionnary to rule them all - waveforms = initWaveforms(ofdm, - scfdma, - ufofdm, - bfofdm, - wola, - fbmc, - ); - # ---------------------------------------------------- - # --- PSD main calculation - # ---------------------------------------------------- - # --- Init plot container - plt = plot(reuse=false); - decim = 1; # decimation for light plots - # --- Iterative PSD generation - for (name,struc) in waveforms - # --- Calculate PSD for the configuration - (fe,psd) = psdWaveform(struc,nbSymb,nbIt); - # Plot the result - plot!(plt,fe[1:decim:end].*samplingFreq,10 .* log10.(psd[1:decim:end]/maximum(psd)),label=name,legend=:topleft); - end - # --- Update plot and adding labels - # Purpose is to zoom out on allocated region. - scsN = (1/1024)*samplingFreq; # Subscarrier spacing (normalized) - rbV = (12*12); # See several RB for psd fall-off - ylims!(-120,5); - xlims!(-rbV*scsN,maximum(allocatedSubcarriers)*scsN+2*12*scsN); - xlabel!("Frequency [MHz]"); - ylabel!("Spectrum"); - display(plt) - end - end - -By running `example_PSD_waveform.main();` a comparison plot between the different PSD can be obtained + module example_PSD_waveform + # ---------------------------------------------------- + # --- Modules + # ---------------------------------------------------- + using DigitalComm + # --- External Modules + using Plots + gr(); + using Printf + using FFTW + # ---------------------------------------------------- + # --- Core functions + # ---------------------------------------------------- + """ psdWaveform.m + --- + Compute the power spectral density (i.e the spectrum here) of the signal parametrized by the waveform structure waveform, for a number of symbol nbSymb. + The frequency allocation is the one inherited from the waveform structure (i.e waveform.allocatedSubcarriers). + # --- Syntax + ( freq,psd ) = psdWaveform(waveform,nbSymb,allocatedSubcarriers); + # --- Input parameters + - waveform : Structure associated to transmitted waveform + - nbSymb : Number of symbol to be transmitted [Int] + - nbIt : Monte carlo parameter for PSD evaluation (should be > 1) + # --- Output parameters + - freq : Vector of frequency evaluation (between -0.5 and 0.5). [Array{Float64,L}] + - psd : Spectrum evaluated on freq [Array{Complex{Float64}},L] + # --- Input parameters + - + # --- Output parameters + - + # --- + # v 1.0 - Robin Gerzaguet. + """ + function psdWaveform(waveform,nbSymb,nbIt) + # ---------------------------------------------------- + # --- PSD calculation + # ---------------------------------------------------- + # --- Getting frequency allocation + allocatedSubcarriers = waveform.allocatedSubcarriers; + # --- Getting number of bits + # First, frequency size + nbSubcarriers = length(allocatedSubcarriers); + # Force a fiven mcs + mcs = 4; # QPSK. + # Deduce number of required bits + nbBits = nbSymb * nbSubcarriers * Int(log2(mcs)); + # --- Init psd evaluator + psd = 0; + # --- Iterative PSD calculation + for iN = 1 : 1 : nbIt + # --- Binary sequence + bitSeq = genBitSequence(nbBits); + # Mapping + qamSeq = bitMappingQAM(mcs,bitSeq); + # --- T/F matrix + qamMat = reshape(qamSeq,nbSubcarriers,nbSymb); + # --- Signal + sigPSD = genSig(qamMat,waveform); + # --- Mean PSD: + psd = psd .+ 1/nbIt*1/length(sigPSD)*abs.(fftshift(fft(sigPSD))).^2; + end + # --- Calculating sampling frequency + # Returns Nyquist frequency + fe = 1; + Basefe = (0:(length(psd) .-1))./length(psd)*fe .-fe/2; + return (Basefe,psd); + end + # ---------------------------------------------------- + # --- Main routine + # ---------------------------------------------------- + function main() + # ---------------------------------------------------- + # --- Overall parameters + # ---------------------------------------------------- + # --- Overall PHY parameters + nbIt = 50; # --- Iteration number + nbSymb = 14; # --- Number of symbols (one frame) + nFFT = 1024; # --- Base FFT size + samplingFreq = 15.36; # --- Frequency value (MHz) + # --- Frequency allocation + #allocatedSubcarriers= getLTEAlloc(nFFT); + #allocatedSubcarriers = (1:12*4); + # 4 RB alloc. 1 RB space. 4 RB allocated + allocatedSubcarriers = [1:12*4; 12*5 .+ (1:12*4)]; + # ---------------------------------------------------- + # --- Waveform contender + # ---------------------------------------------------- + # --- Init OFDM structure + ofdm = initOFDM( + nFFT, # --- nFFT : FFT size + 72, # --- nCP : CP size + allocatedSubcarriers # --- allocatedSubcarriers : Subcarrier allocation + ); + # --- Init SCFDMA structure + scfdma = initSCFDMA( + nFFT, # --- nFFT : FFT size + 72, # --- nCP : CP size + allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation + 12; # --- sizeDFT : DFT preprocessing size + ); + # --- Init UF-OFDM structure + ufofdm = initUFOFDM( + nFFT, # --- nFFT : FFT size + 73, # --- L : Filter length (same size +1 due to conv) + allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation + applyPD=1, # --- applyPD : Do predistortion at Tx stage + attenuation=40, # --- attenuation : Filter attenuation in dB + ); + # --- Init BF-OFDM structure + bfofdm = initBFOFDM( + 32, # --- nFBMC : PPN size (max number of carriers) + 64, # --- nOFDM : Precoder size (OFDM sizer) + 3, # --- K : Overlapping factor + 9, # --- GI : CP size of precoder + 0.5, # --- δ : compression factor + allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation + "gaussian", # --- filterName : Pulse shape name + BT=0.36, # --- BT : Potential BT value for Gaussian + filterStopBand = 110, # --- filterStopBand : DC stopband value + fS=[], # --- fS : Potential frequency coefficient for FS filter + nFFT= 1024, # --- nFFT : associated FFT value in Rx + nCP= 72, # --- nCP : extended CP size + ); + # --- Init WOLA-OFDM structure + wola = initWOLA( + nFFT, # --- nFFT : FFT size + 72, # --- nCP : CP size + allocatedSubcarriers, # --- allocatedSubcarriers : Subcarrier allocation + "triangle", # --- Window type @Tx side + 20, # --- Window size @Tx side + "triangle", # --- Window type @Rx side + 20, # --- Window size @Rx side + ); + fbmc = initFBMC( + nFFT, # --- nFFT : FFT size + 4, # --- K : Overlapping factor + allocatedSubcarriers # --- allocatedSubcarriers : Subcarrier allocation + ); + # ---------------------------------------------------- + # --- Merging structures + # ---------------------------------------------------- + # Create a dictionnary to rule them all + waveforms = initWaveforms(ofdm, + scfdma, + ufofdm, + bfofdm, + wola, + fbmc, + ); + # ---------------------------------------------------- + # --- PSD main calculation + # ---------------------------------------------------- + # --- Init plot container + plt = plot(reuse=false); + decim = 1; # decimation for light plots + # --- Iterative PSD generation + for (name,struc) in waveforms + # --- Calculate PSD for the configuration + (fe,psd) = psdWaveform(struc,nbSymb,nbIt); + # Plot the result + plot!(plt,fe[1:decim:end].*samplingFreq,10 .* log10.(psd[1:decim:end]/maximum(psd)),label=name,legend=:topleft); + end + # --- Update plot and adding labels + # Purpose is to zoom out on allocated region. + scsN = (1/1024)*samplingFreq; # Subscarrier spacing (normalized) + rbV = (12*12); # See several RB for psd fall-off + ylims!(-120,5); + xlims!(-rbV*scsN,maximum(allocatedSubcarriers)*scsN+2*12*scsN); + xlabel!("Frequency [MHz]"); + ylabel!("Spectrum"); + display(plt) + end + end -![PSD](./../img/psd.png) +By running `example_PSD_waveform.main();` a comparison plot between the different PSD can be obtained - - +![PSD](./../img/psd.png) diff --git a/docs/src/base.md b/docs/src/base.md index 632598c..656c018 100644 --- a/docs/src/base.md +++ b/docs/src/base.md @@ -1,97 +1,89 @@ - - -## Common functions +## Common functions ```@autodocs Modules = [DigitalComm] Pages = ["DigitalComm.jl"] Order = [:function, :type] -Depth = 1 ``` - -## Quadrature Amplitude Modulation +## Quadrature Amplitude Modulation ```@autodocs Modules = [DigitalComm] Pages = ["genBitSequence.jl","bitMapping.jl","bitDeMapping.jl","hardConstellation.jl","symbolDemapper.jl"] Order = [:function, :type] -Depth = 1 ``` +## Channels + +```@autodocs +Modules = [DigitalComm] +Pages = ["Channel/addNoise.jl","Channel/rayleighChan.jl","Channel/getChannel.jl", "Channel/addCFO.jl"] +Order = [:function, :type] +``` -## Channels +## Windows and filters ```@autodocs Modules = [DigitalComm] -Pages = ["Channel/addNoise.jl","Channel/rayleighChan.jl","Channel/getChannel.jl"] +Pages = ["raisedCosine.jl", "UFOFDM/filterUFOFDM.jl", "WOLA/getWolaWindow.jl"] Order = [:function, :type] -Depth = 1 ``` -# Waveforms +# Waveforms -## Common functions +## Common functions ```@autodocs Modules = [DigitalComm] Pages = ["genSig.jl","genZCSequence.jl","getLTEAlloc.jl"] Order = [:function, :type] -Depth = 0 ``` -## BF-OFDM +## BF-OFDM ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/BFOFDM/BFOFDM_filter.jl","Waveforms/BFOFDM/bfofdmSigGen.jl","Waveforms/BFOFDM/bfofdmSigDecode.jl","Waveforms/BFOFDM/carrierManipulation.jl"] Order = [:function, :type] -Depth = 0 ``` -## FBMC +## FBMC ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/FBMC/fbmcSigGen.jl","Waveforms/FBMC/fbmcSigDecode.jl"] Order = [:function, :type] -Depth = 0 ``` - -## OFDM +## OFDM ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/OFDM/ofdmSigGen.jl","Waveforms/OFDM/ofdmSigDecode.jl"] Order = [:function, :type] -Depth = 0 ``` -## SC-FDMA +## SC-FDMA ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/UFOFDM/ufofdmSigGen.jl","Waveforms/UFOFDM/ufofdmSigDecode.jl"] Order = [:function, :type] -Depth = 0 ``` -## UF-OFDM +## UF-OFDM ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/filterUFOFDM.jl","Waveforms/SCFDMA/scfdmaSigGen.jl","Waveforms/SCFDMA/scfdmaSigDecode.jl"] Order = [:function, :type] -Depth = 0 ``` - -## WOLA +## WOLA ```@autodocs Modules = [DigitalComm] Pages = ["Waveforms/WOLA/wolaSigGen.jl","Waveforms/WOLA/wolaSigDecode.jl"] Order = [:function, :type] -Depth = 0 ``` diff --git a/docs/src/index.md b/docs/src/index.md index 5729cb3..0a57d9e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,30 +1,30 @@ -# DigitalComm.jl documentation - -## Summary +# DigitalComm.jl documentation +## Summary This package aims to provide some usefull tools to manipulate digital -communication blocks in Julia. -Currently, the package support the following elements -- Bit manipulation - * Generation of random binary sequence - * Conversion between binary sequences and octal sequences +communication blocks in Julia. +Currently, the package support the following elements + +- Bit manipulation + - Generation of random binary sequence + - Conversion between binary sequences and octal sequences - Modulation // demodulation - * Quadrature Amplitude Modulation (QAM) with 4-QAM (QPSK), 16-QAM, 64-QAM and 256-QAM. - * Hard demapper for the x-QAM formats - * Max log Soft demapper for the x-QAM formats -- Single carrier pulses shapes - * Raised Cosine pulse shape - * Square root raised Cosine pulse shape -- Multicarrier Waveform generation and decoding - * Support of multicarrier Waveforms: OFDM, UF-OFDM, WOLA, BF-OFDM + - Quadrature Amplitude Modulation (QAM) with 4-QAM (QPSK), 16-QAM, 64-QAM and 256-QAM. + - Hard demapper for the x-QAM formats + - Max log Soft demapper for the x-QAM formats +- Single carrier pulses shapes + - Raised Cosine pulse shape + - Square root raised Cosine pulse shape +- Multicarrier Waveform generation and decoding + - Support of multicarrier Waveforms: OFDM, UF-OFDM, WOLA, BF-OFDM ## Installation The package can be installed with the Julia package manager. From the Julia REPL, type `]` to enter the Pkg REPL mode and run: -``` +```pkg pkg> add DigitalComm ``` @@ -34,9 +34,7 @@ Or, equivalently, via the `Pkg` API: julia> import Pkg; Pkg.add("DigitalComm") ``` - -## Documentation - +## Documentation - The base documentation with the different functions can be found [in the base section](base.md) - Different examples are described in [in the example section](example.md). Other examples are provided in the example subfolder of the project. diff --git a/readme.md b/readme.md index da8e830..dc3362d 100644 --- a/readme.md +++ b/readme.md @@ -1,36 +1,37 @@
-Makie.jl + Makie.jl
-# DigitalCom.jl +# DigitalComm.jl [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliatelecom.github.io/DigitalComm.jl/dev/index.html) [![Run tests](https://github.com/JuliaTelecom/DigitalComm.jl/actions/workflows/test.yml/badge.svg)](https://github.com/JuliaTelecom/DigitalComm.jl/actions/workflows/test.yml) -## Purpose +## Purpose This package aims to provide some usefull tools to manipulate digital -communication blocks in Julia. -Currently, the package support the following elements -- Bit manipulation - * Generation of random binary sequence - * Conversion between binary sequences and octal sequences +communication blocks in Julia. +Currently, the package support the following elements + +- Bit manipulation + - Generation of random binary sequence + - Conversion between binary sequences and octal sequences - Modulation // demodulation - * Quadrature Amplitude Modulation (QAM) with 4-QAM (QPSK), 16-QAM, 64-QAM and 256-QAM. - * Hard demapper for the x-QAM formats - * Max log Soft demapper for the x-QAM formats -- Single carrier pulses shapes - * Raised Cosine pulse shape - * Square root raised Cosine pulse shape -- Multicarrier Waveform generation and decoding - * Support of multicarrier Waveforms: OFDM, UF-OFDM, WOLA, BF-OFDM + - Quadrature Amplitude Modulation (QAM) with 4-QAM (QPSK), 16-QAM, 64-QAM and 256-QAM. + - Hard demapper for the x-QAM formats + - Max log Soft demapper for the x-QAM formats +- Single carrier pulses shapes + - Raised Cosine pulse shape + - Square root raised Cosine pulse shape +- Multicarrier Waveform generation and decoding + - Support of multicarrier Waveforms: OFDM, UF-OFDM, WOLA, BF-OFDM ## Installation The package can be installed with the Julia package manager. From the Julia REPL, type `]` to enter the Pkg REPL mode and run: -``` +```pkg pkg> add DigitalComm ``` diff --git a/src/Channel/getChannel.jl b/src/Channel/getChannel.jl index b0659c1..6d60ec4 100644 --- a/src/Channel/getChannel.jl +++ b/src/Channel/getChannel.jl @@ -11,7 +11,7 @@ Channel model object - samplingFreq : Sampling frequency [Union{Int,Float64}] - speed : Desired speed (km/h) [Union{Int,Float64}] - powerProfile : Distribution of power values in dB [Array{Float64}] -- delayProfile : Distribution of delay values in s [Array{Float64}] (same size as powerProfile) +- delayProfile : Distribution of delay values in s [Array{Float64}], same size as powerProfile - dopplerFreq : Doppler frequency (inherited from samplingFreq and speed) - delaySpread : Max support of CIR in samples [Int] # --- diff --git a/src/raisedCosine.jl b/src/raisedCosine.jl index 76474b4..f22e2db 100644 --- a/src/raisedCosine.jl +++ b/src/raisedCosine.jl @@ -6,11 +6,19 @@ SRRC definition is based on [1] \n Syntax \n h = raisedCosine(N,beta,ovS) Input parameters \n -- N : Symbol span (Int16) +- N : Symbol span (Int16) - beta : Roll-off factor (Float64) -- ovS : Oversampling rate (Int16) +- ovS : Oversampling rate (Int16) + +## Examples +```jldoctest +julia> h = raisedCosine(12,0.5,16); + +julia> length(h) +385 +``` """ -function raisedCosine(N,beta,ovS) +function raisedCosine(N,beta,ovS)::Vector{Float64} # --- Final size of filter nbTaps = 2 * N * ovS + 1; # --- Init output @@ -37,17 +45,25 @@ end """ Returns the Finite Impulse Response of a Square Root Raised Cosine (SRRC) filter. \n The filter is defined by its span (evaluated in number of symbol N), its Roll-Off factor and its oversampling factor. The span corresponds to the number of symbol affected by filter before and after the center point.\n -Output is a Vector{Float64} array of size L= 2KN+1\n +Output is a `Vector{Float64}` array of size L= 2KN+1\n SRRC definition is based on [1]\n [1] 3GPP TS 25.104 V6.8.0 (2004-12). http://www.3gpp.org/ftp/ Specs/archive/25_series/25.104/25104-680.zip\n Syntax\n h = sqrtRaisedCosine(N,beta,ovS) \n Input parameters \n -- N : Symbol span (Int16) +- N : Symbol span (Int16) - beta : Roll-off factor (Float64) -- ovS : Oversampling rate (Int16) +- ovS : Oversampling rate (Int16) + +## Examples +```jldoctest +julia> h = sqrtRaisedCosine(12,0.5,16); + +julia> length(h) +385 +``` """ -function sqrtRaisedCosine(N,beta,ovS) +function sqrtRaisedCosine(N,beta,ovS)::Vector{Float64} # --- Final size of filter nbTaps = 2 * N * ovS + 1; # --- Init output diff --git a/test/runtests.jl b/test/runtests.jl index 58faab4..41f7cbc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,13 +1,14 @@ # ---------------------------------------------------- # --- Tests # ---------------------------------------------------- -import Printf -import Test +import Printf, Test + +# Run DocTests +include("test_doctest.jl") # Binary sequence include("test_genBitSequence.jl"); - # Bit Mapping include("test_bitMapping.jl"); diff --git a/test/test_doctest.jl b/test/test_doctest.jl new file mode 100644 index 0000000..a5e2e5d --- /dev/null +++ b/test/test_doctest.jl @@ -0,0 +1,5 @@ +using Documenter, DigitalComm + +# Runs the doctests (the jldoctest) in funciton documentation. +DocMeta.setdocmeta!(DigitalComm, :DocTestSetup, :(using DigitalComm); recursive=true) +Documenter.doctest(DigitalComm) \ No newline at end of file