From 36ffd6a487409386f32a7f224554a785aa284075 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Mon, 13 Jun 2022 15:13:46 +0200 Subject: [PATCH 1/8] Adding first version of CDMA encoder, using ovsf --- examples/example_cdma.jl | 22 +++++++++ src/DigitalComm.jl | 50 +++++++++---------- src/Waveforms/CDMA/cdmaSigGen.jl | 85 ++++++++++++++++++++++++++++++++ test/test_waveforms.jl | 26 ++++++++++ 4 files changed, 156 insertions(+), 27 deletions(-) create mode 100644 examples/example_cdma.jl create mode 100644 src/Waveforms/CDMA/cdmaSigGen.jl diff --git a/examples/example_cdma.jl b/examples/example_cdma.jl new file mode 100644 index 0000000..cd2aea1 --- /dev/null +++ b/examples/example_cdma.jl @@ -0,0 +1,22 @@ +using DigitalComm + + +# ---------------------------------------------------- +# --- Parameters +# ---------------------------------------------------- +nbBits = 32768 +mcs = 4 +nbUsers = 16 + +# ---------------------------------------------------- +# --- CDMA Generation +# ---------------------------------------------------- +# --- Binary sequence +bitSeq = genBitSequence(nbBits); +# Mapping +qamSeq = bitMappingQAM(mcs,bitSeq); +nbSymb = length(qamSeq) ÷ nbUsers +# --- T/F matrix +qamMat = reshape(qamSeq,nbSymb,nbUsers); +# --- Signal +sigId = cdmaSigGen(qamMat,nbUsers,:ovsf) diff --git a/src/DigitalComm.jl b/src/DigitalComm.jl index 62da9c8..33fe8f7 100644 --- a/src/DigitalComm.jl +++ b/src/DigitalComm.jl @@ -64,20 +64,15 @@ export sqrtRaisedCosine # ---------------------------------------------------- # --- Q function definition import SpecialFunctions.erfc -""" ---- -Returns the Q function used for Bit error rate computation in digital system - Q(x)= 1/2 erfc(x/sqrt(2)) -erfc is the Complexmplementary error function, i.e. the accurate version of 1-erf(x) for large x -erfc is inherited from DSP -# --- Syntax -y = qfunc(x) -# --- Input parameters +"""Returns the Q function used for Bit error rate computation in digital system \\ + Q(x)= 1/2 erfc(x/sqrt(2)) \\ +erfc is the Complexmplementary error function, i.e. the accurate version of 1-erf(x) for large x \\ +erfc is inherited from DSP\\ +y = qfunc(x) \\ +Input parameters - x: Input [Float64] -# --- Output parameters +Output parameters - y: Q(x)[Float64] -# --- -# v 1.0 - Robin Gerzaguet. """ function qFunc(x) return 1/2 * erfc.( x / sqrt(2)); @@ -87,21 +82,18 @@ export qFunc; """ ---- -Returns the Signal to interference ratio expressed in dB (or in linear) between a obersvation signal d(n) and a reference signal u(n) -The ratio is expressed as 10*log10( E[ || d(n) - u(n) || / E[||u(n)||^2] ) - with E the expectation wrt to time - The 2 vectors d and u should have the same length L -# --- Syntax - sir = getSIR( d, u , type="dB") -# --- Input parameter -- d : Observation signal [Array{Any}] -- u : Reference signal [Array{Any}] -- type: Output unit [String]: "dB" or "Linear" (default, "dB") -# --- Output parameters +Returns the Signal to interference ratio expressed in dB (or in linear) between a obersvation signal d(n) and a reference signal u(n) \\ +The ratio is expressed as 10*log10( E[ || d(n) - u(n) || / E[||u(n)||^2] ) \\ + with E the expectation wrt to time \\ +The 2 vectors d and u should have the same length L \\ + +sir = getSIR( d, u , type="dB")\\ +Input parameters +- d : Observation signal [`Array`] +- u : Reference signal [`Array`] +- type: Output unit [`String`]: "dB" or "Linear" (default, "dB") +Output parameters - sir : Signal to interference ratio in unit `type` -# --- -# v 1.0 - Robin Gerzaguet. """ function getSIR(d,u,type="dB") # --- Setting type @@ -233,7 +225,11 @@ include("./Waveforms/FBMC/fbmcSigDecode.jl"); export oqamDemapping export fbmcSigDecode - +# CDMA Generation +include("./Waveforms/CDMA/CDMASigGen.jl") +export ovsf +export cdmaSigGen +export cdmaSigGen! # --- Global waveform alias include("./Waveforms/genSig.jl"); diff --git a/src/Waveforms/CDMA/cdmaSigGen.jl b/src/Waveforms/CDMA/cdmaSigGen.jl new file mode 100644 index 0000000..045a7f1 --- /dev/null +++ b/src/Waveforms/CDMA/cdmaSigGen.jl @@ -0,0 +1,85 @@ +""" Orthogonal Variable Spreading Factor (OVSF) code generation +It generates N codes that are orthogonal and follows the OVSF code pattern +codes = ovsf(N,initState::UInt8=1) \\ +Input +- N = Number of generated codes (should be a power of 2) +- initState : Initial state value (default 1) +Output +- codes : Matrix of size code_size x N with type UInt8 +""" +function ovsf(N,initState::Int8=Int8(1)) + n = Int(log2(N)) + C = [initState] + for _ in 0 : n - 1 + newC = Int8[] + for c in eachrow(C) + c_0 = vcat(c,c) + c_1 = vcat(c,-c) + newC = _hcat_empty(newC,c_0) + newC = _hcat_empty(newC,c_1) + end + C = newC + end + return C +end + +# A custom concat operator that handle empty input vector +function _hcat_empty(a,b) + isempty(a) ? b : hcat(a,b) +end + + +""" Generates a Code Division Multiplexing Access signal from a Symbol matrix `qamMat` and using code family `code`. \\ +cdmaSigGen(qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userMask=nothing) \\ +Input parameters +- qamMat : Matrix of symbols of size nbSymb x nbActiveUser. Each column corresponds to a user stream and there is at least nbActiveUser. nbActiveUser should be ≤ than the number of code we have +- nbUsers : Maximal number of user that can be multiplexed. Used for code generation +- code : Type of code used as a Symbol. We support `:ovsf` +- userMask: Optional parameter, a vector of the code used. Should be of size nbActiveUser. For example, if we have 16 max users but we only want to encode 1000 symbols for user 2 and user 12, we should call `cdmaSigGen` with a matrix `qamMat` of size (1000,2), `nbUsers` = 16 and `userMask` = [2;12] +""" +function cdmaSigGen!(sigOut::AbstractVector,qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userMask=nothing) where T + # ---------------------------------------------------- + # --- Code generation + # ---------------------------------------------------- + if code == :ovsf + # --- Generate the code base + c = ovsf(nbUsers) + else + @error "Code $code is unsupported. We currently inly support OVSF" + end + nbActiveUser = size(qamMat,2) + nbSymb = size(qamMat,1) + # Check we can encode users (more codes than uers) + @assert nbActiveUser ≤ nbUsers "Number of active users ($nbActiveUser) should be lower than the code size ($nbUsers)." + # Check output is of enough size + @assert length(sigOut) == nbUsers * size(qamMat,1) "Size of output vector ($(length(sigOut)) does not match input size x code size ($(nbSymb)x$(nbUsers)= $(nbUsers*size(qamMat,1)))" + # Code selection + if isnothing(userMask) + # We use the first codes as codeset + userMask = 1 : nbActiveUser + end + # ---------------------------------------------------- + # --- Scramling + # ---------------------------------------------------- + for n = 1 : 1 : nbActiveUser + # Apply the spreading and accumulate + _spread_accum!(sigOut,qamMat[:,n],c[:,userMask[n]],nbUsers) + end + return sigOut +end +function cdmaSigGen(qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userMask=nothing) where T + sigOut = zeros(T,size(qamMat,1)*nbUsers) + cdmaSigGen!(sigOut,qamMat,nbUsers,code,userMask) +return sigOut +end + +""" Apply a sreaping of input signal `seq` using `code` and accumulate the result in `tmp` +""" +function _spread_accum!(tmp,seq,code,sF) + @inbounds @simd for n ∈ eachindex(seq) + for k ∈ 1 : sF + tmp[(n-1)*sF + k] += seq[n] .* code[sF] + end + end +end + diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index 55de3c6..d82265c 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -111,3 +111,29 @@ for (name,struc) in waveforms end end + + +@testset "OVSF CDMA " begin + # Testing OVSF code + for N = [4,8,16,64] + c = ovsf(N) + for cn ∈ 1 : N + for ck ∈ 1 : N + # --- Code correlation + γ = sum(c[cn,:] .* conj(c[ck,:]) ) + if cn == ck + # Autocorrelation + @test γ == N + else + # Zero correlation + @test γ == 0 + end + end + end + end +end + + +@testset "CDMA generator" begin + +end From 4e5e12dadca8ab34a4404626a342e2ce853abed7 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Tue, 14 Jun 2022 21:20:00 +0200 Subject: [PATCH 2/8] Code should be apply with iteration also -_- --- src/Waveforms/CDMA/cdmaSigGen.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Waveforms/CDMA/cdmaSigGen.jl b/src/Waveforms/CDMA/cdmaSigGen.jl index 045a7f1..8297fba 100644 --- a/src/Waveforms/CDMA/cdmaSigGen.jl +++ b/src/Waveforms/CDMA/cdmaSigGen.jl @@ -73,12 +73,12 @@ function cdmaSigGen(qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userM return sigOut end -""" Apply a sreaping of input signal `seq` using `code` and accumulate the result in `tmp` +""" Apply a spreading of input signal `seq` using `code` and accumulate the result in `tmp` """ function _spread_accum!(tmp,seq,code,sF) @inbounds @simd for n ∈ eachindex(seq) for k ∈ 1 : sF - tmp[(n-1)*sF + k] += seq[n] .* code[sF] + tmp[(n-1)*sF + k] += seq[n] * code[k] end end end From bf3abdf5217a3c3ce9b0cf89104411a968dedec5 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Tue, 14 Jun 2022 21:52:27 +0200 Subject: [PATCH 3/8] Adding test for cdmaSigGen and basic Tx example --- examples/example_cdma.jl | 7 ++++--- test/test_waveforms.jl | 32 +++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/examples/example_cdma.jl b/examples/example_cdma.jl index cd2aea1..09f4109 100644 --- a/examples/example_cdma.jl +++ b/examples/example_cdma.jl @@ -7,16 +7,17 @@ using DigitalComm nbBits = 32768 mcs = 4 nbUsers = 16 +nbActiveUsers = 16 # ---------------------------------------------------- # --- CDMA Generation # ---------------------------------------------------- # --- Binary sequence -bitSeq = genBitSequence(nbBits); +bitSeq = genBitSequence(nbBits) # Mapping qamSeq = bitMappingQAM(mcs,bitSeq); -nbSymb = length(qamSeq) ÷ nbUsers +nbSymb = length(qamSeq) ÷ nbActiveUsers # --- T/F matrix -qamMat = reshape(qamSeq,nbSymb,nbUsers); +qamMat = reshape(qamSeq,nbSymb,nbActiveUsers); # --- Signal sigId = cdmaSigGen(qamMat,nbUsers,:ovsf) diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index d82265c..764fe34 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -135,5 +135,35 @@ end @testset "CDMA generator" begin - + N = 16 + C = ovsf(N) + # --- Spreading + for n ∈ 1 : N + c = C[:,n] + # Check spreading with one as input, we should obtain the code + in = [1] + out = zeros(16) + DigitalComm._spread_accum!(out,in,c,N) + @test all(out .== c) + # Check that accumulation is Ok + DigitalComm._spread_accum!(out,in,c,N) + @test all(out .== 2 .*c) + end + # --- True generator + nS = 100 + qamMat = ones(nS,N) + sigId = cdmaSigGen(qamMat,N,:ovsf) + @test sigId isa Vector + @test length(sigId) == nS * N + for n = 1 : 1 : N + @test sigId[n] == sum(C[n,:]) + end + # --- Generate only user 8 + qamMat = ones(nS,1) + sigId = cdmaSigGen(qamMat,N,:ovsf,[8]) + @test sigId isa Vector + @test length(sigId) == nS * N + for n = 1 : 1 : N + @test sigId[n] == C[n,8] + end end From 404fd0c418de054dc26700ba8c6b318b7281c7ec Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Wed, 15 Jun 2022 11:29:40 +0200 Subject: [PATCH 4/8] Adding CDMA receiver --- examples/example_cdma.jl | 19 +++++++++++++++++++ src/DigitalComm.jl | 5 ++++- test/test_waveforms.jl | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/examples/example_cdma.jl b/examples/example_cdma.jl index 09f4109..0304438 100644 --- a/examples/example_cdma.jl +++ b/examples/example_cdma.jl @@ -21,3 +21,22 @@ nbSymb = length(qamSeq) ÷ nbActiveUsers qamMat = reshape(qamSeq,nbSymb,nbActiveUsers); # --- Signal sigId = cdmaSigGen(qamMat,nbUsers,:ovsf) + + +# ---------------------------------------------------- +# --- Ideal decoding +# ---------------------------------------------------- +# --- Decoding +qamRx = cdmaSigDecode(sigId,nbUsers,:ovsf,1:nbActiveUsers) +sir = getSIR(qamRx,qamMat) +println("SIR between Tx and Rx sequence (no noise) is $sir dB") + + +# ---------------------------------------------------- +# --- Decoding with noise +# ---------------------------------------------------- +snr = 30 +sigRx,_ = addNoise(sigId,snr) +qamRx = cdmaSigDecode(sigRx,nbUsers,:ovsf,1:nbActiveUsers) +sir = getSIR(qamRx,qamMat) +println("SIR between Tx and Rx sequence ($snr dB additive noise) is $sir dB") diff --git a/src/DigitalComm.jl b/src/DigitalComm.jl index 33fe8f7..bdf5e36 100644 --- a/src/DigitalComm.jl +++ b/src/DigitalComm.jl @@ -226,10 +226,13 @@ export oqamDemapping export fbmcSigDecode # CDMA Generation -include("./Waveforms/CDMA/CDMASigGen.jl") +include("./Waveforms/CDMA/cdmaSigGen.jl") export ovsf export cdmaSigGen export cdmaSigGen! +include("./Waveforms/CDMA/cdmaSigDecode.jl") +export cdmaSigDecode + # --- Global waveform alias include("./Waveforms/genSig.jl"); diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index 764fe34..4777e66 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -167,3 +167,25 @@ end @test sigId[n] == C[n,8] end end + + +@testset "CDMA receiver" begin + nS = 32768 + N = 16 + mcs = 4 + nbSymb = nS ÷ N ÷ Int(log2(mcs)) + # Mapping + bitSeq = genBitSequence(nS); + qamSeq = bitMappingQAM(mcs,bitSeq); + # --- T/F matrix + qamMat = reshape(qamSeq,nbSymb,N); + sigId = cdmaSigGen(qamMat,N,:ovsf) + # Testing global decoding method + qamRx = cdmaSigDecode(sigId,N,:ovsf,1:N) + @test all(qamRx .≈ qamMat) + # Testing one user decoding + for n = 1 : 1 : N + qamRx = cdmaSigDecode(sigId,N,:ovsf,[n]) + @test all(qamRx .≈ qamMat[:,n]) + end +end From 5b635de853559667626d78a7228ff17c5cbf0396 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Wed, 15 Jun 2022 11:46:38 +0200 Subject: [PATCH 5/8] Change Code x time matrix to have a similar pattern as a Freq x time matrix in OFDM --- examples/example_cdma.jl | 2 +- src/Waveforms/CDMA/cdmaSigDecode.jl | 35 +++++++++++++++++++++++++++++ src/Waveforms/CDMA/cdmaSigGen.jl | 13 ++++++----- test/test_waveforms.jl | 8 +++---- 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 src/Waveforms/CDMA/cdmaSigDecode.jl diff --git a/examples/example_cdma.jl b/examples/example_cdma.jl index 0304438..a1e5ac9 100644 --- a/examples/example_cdma.jl +++ b/examples/example_cdma.jl @@ -18,7 +18,7 @@ bitSeq = genBitSequence(nbBits) qamSeq = bitMappingQAM(mcs,bitSeq); nbSymb = length(qamSeq) ÷ nbActiveUsers # --- T/F matrix -qamMat = reshape(qamSeq,nbSymb,nbActiveUsers); +qamMat = reshape(qamSeq,nbActiveUsers,nbSymb); # --- Signal sigId = cdmaSigGen(qamMat,nbUsers,:ovsf) diff --git a/src/Waveforms/CDMA/cdmaSigDecode.jl b/src/Waveforms/CDMA/cdmaSigDecode.jl new file mode 100644 index 0000000..7fada74 --- /dev/null +++ b/src/Waveforms/CDMA/cdmaSigDecode.jl @@ -0,0 +1,35 @@ + + +function cdmaSigDecode(sig::AbstractVector{T},nbUser,code::Symbol,userMask=nothing) where T + # --- Define constant + nbChips = length(sig) + nbSymb = nbChips ÷ nbUser + # --- Define code + if code == :ovsf + c = ovsf(nbUser) + else + @error "Code $code is not supported" + end + if isnothing(userMask) + userMask = 1:nbUser + end + nbActiveUsers = length(userMask) + qamRx = zeros(T,nbActiveUsers,nbSymb) + tmp = zeros(T,nbSymb) # To be optimized as a view of qamRx + for n ∈ eachindex(userMask) + despreading!(tmp,sig,nbUser,c[:,userMask[n]]) + qamRx[n,:] .= tmp + end + return qamRx +end + +function despreading!(tmp,sig,nbUser,code) + for n ∈ eachindex(tmp) + tmp[n] = 0 + for k ∈ 1 : nbUser + tmp[n] += sig[(n-1)*nbUser + k] * code[k] + end + tmp[n] = tmp[n] / nbUser + end +end + diff --git a/src/Waveforms/CDMA/cdmaSigGen.jl b/src/Waveforms/CDMA/cdmaSigGen.jl index 8297fba..da74131 100644 --- a/src/Waveforms/CDMA/cdmaSigGen.jl +++ b/src/Waveforms/CDMA/cdmaSigGen.jl @@ -6,6 +6,7 @@ Input - initState : Initial state value (default 1) Output - codes : Matrix of size code_size x N with type UInt8 +[1] Yuh-Shyan Chen and Ting-Lung Lin, "Code placement and replacement schemes for WCDMA Rotated-OVSF code tree management," in IEEE Transactions on Mobile Computing, vol. 5, no. 3, pp. 224-239, March 2006, doi: 10.1109/TMC.2006.30. """ function ovsf(N,initState::Int8=Int8(1)) n = Int(log2(N)) @@ -32,7 +33,7 @@ end """ Generates a Code Division Multiplexing Access signal from a Symbol matrix `qamMat` and using code family `code`. \\ cdmaSigGen(qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userMask=nothing) \\ Input parameters -- qamMat : Matrix of symbols of size nbSymb x nbActiveUser. Each column corresponds to a user stream and there is at least nbActiveUser. nbActiveUser should be ≤ than the number of code we have +- qamMat : Matrix of symbols of size nbActiveUser x nbSymb. Each line corresponds to a user stream and there is at least nbActiveUser colunm. nbActiveUser should be ≤ than the number of code we have - nbUsers : Maximal number of user that can be multiplexed. Used for code generation - code : Type of code used as a Symbol. We support `:ovsf` - userMask: Optional parameter, a vector of the code used. Should be of size nbActiveUser. For example, if we have 16 max users but we only want to encode 1000 symbols for user 2 and user 12, we should call `cdmaSigGen` with a matrix `qamMat` of size (1000,2), `nbUsers` = 16 and `userMask` = [2;12] @@ -47,12 +48,12 @@ function cdmaSigGen!(sigOut::AbstractVector,qamMat::AbstractMatrix{T},nbUsers::N else @error "Code $code is unsupported. We currently inly support OVSF" end - nbActiveUser = size(qamMat,2) - nbSymb = size(qamMat,1) + nbActiveUser = size(qamMat,1) + nbSymb = size(qamMat,2) # Check we can encode users (more codes than uers) @assert nbActiveUser ≤ nbUsers "Number of active users ($nbActiveUser) should be lower than the code size ($nbUsers)." # Check output is of enough size - @assert length(sigOut) == nbUsers * size(qamMat,1) "Size of output vector ($(length(sigOut)) does not match input size x code size ($(nbSymb)x$(nbUsers)= $(nbUsers*size(qamMat,1)))" + @assert length(sigOut) == nbUsers * nbSymb "Size of output vector ($(length(sigOut)) does not match input size x code size ($(nbSymb)x$(nbUsers)= $(nbUsers*nbSymb))" # Code selection if isnothing(userMask) # We use the first codes as codeset @@ -63,12 +64,12 @@ function cdmaSigGen!(sigOut::AbstractVector,qamMat::AbstractMatrix{T},nbUsers::N # ---------------------------------------------------- for n = 1 : 1 : nbActiveUser # Apply the spreading and accumulate - _spread_accum!(sigOut,qamMat[:,n],c[:,userMask[n]],nbUsers) + _spread_accum!(sigOut,qamMat[n,:],c[:,userMask[n]],nbUsers) end return sigOut end function cdmaSigGen(qamMat::AbstractMatrix{T},nbUsers::Number,code::Symbol,userMask=nothing) where T - sigOut = zeros(T,size(qamMat,1)*nbUsers) + sigOut = zeros(T,size(qamMat,2)*nbUsers) cdmaSigGen!(sigOut,qamMat,nbUsers,code,userMask) return sigOut end diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index 4777e66..32da31b 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -151,7 +151,7 @@ end end # --- True generator nS = 100 - qamMat = ones(nS,N) + qamMat = ones(N,nS) sigId = cdmaSigGen(qamMat,N,:ovsf) @test sigId isa Vector @test length(sigId) == nS * N @@ -159,7 +159,7 @@ end @test sigId[n] == sum(C[n,:]) end # --- Generate only user 8 - qamMat = ones(nS,1) + qamMat = ones(1,nS) # Equivalent to a one subcarrier OFDM system sigId = cdmaSigGen(qamMat,N,:ovsf,[8]) @test sigId isa Vector @test length(sigId) == nS * N @@ -178,7 +178,7 @@ end bitSeq = genBitSequence(nS); qamSeq = bitMappingQAM(mcs,bitSeq); # --- T/F matrix - qamMat = reshape(qamSeq,nbSymb,N); + qamMat = reshape(qamSeq,N,nbSymb); sigId = cdmaSigGen(qamMat,N,:ovsf) # Testing global decoding method qamRx = cdmaSigDecode(sigId,N,:ovsf,1:N) @@ -186,6 +186,6 @@ end # Testing one user decoding for n = 1 : 1 : N qamRx = cdmaSigDecode(sigId,N,:ovsf,[n]) - @test all(qamRx .≈ qamMat[:,n]) + @test all(qamRx[1,:] .≈ qamMat[n,:]) end end From a1044cb17e28394e7a14fb7b02886ddbda6bbcd6 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Wed, 15 Jun 2022 12:09:03 +0200 Subject: [PATCH 6/8] Addinc StrucCDMA and associated dispatch --- src/DigitalComm.jl | 1 + src/Waveforms/CDMA/cdmaSigDecode.jl | 21 ++++++++++++--------- src/Waveforms/CDMA/cdmaSigGen.jl | 20 ++++++++++++++++++++ test/test_waveforms.jl | 13 +++++++++++++ 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/DigitalComm.jl b/src/DigitalComm.jl index bdf5e36..a8ed105 100644 --- a/src/DigitalComm.jl +++ b/src/DigitalComm.jl @@ -227,6 +227,7 @@ export fbmcSigDecode # CDMA Generation include("./Waveforms/CDMA/cdmaSigGen.jl") +export initCDMA export ovsf export cdmaSigGen export cdmaSigGen! diff --git a/src/Waveforms/CDMA/cdmaSigDecode.jl b/src/Waveforms/CDMA/cdmaSigDecode.jl index 7fada74..4e04b1e 100644 --- a/src/Waveforms/CDMA/cdmaSigDecode.jl +++ b/src/Waveforms/CDMA/cdmaSigDecode.jl @@ -1,35 +1,38 @@ -function cdmaSigDecode(sig::AbstractVector{T},nbUser,code::Symbol,userMask=nothing) where T +function cdmaSigDecode(sig::AbstractVector{T},nbUsers,code::Symbol,userMask=nothing) where T # --- Define constant nbChips = length(sig) - nbSymb = nbChips ÷ nbUser + nbSymb = nbChips ÷ nbUsers # --- Define code if code == :ovsf - c = ovsf(nbUser) + c = ovsf(nbUsers) else @error "Code $code is not supported" end if isnothing(userMask) - userMask = 1:nbUser + userMask = 1:nbUsers end nbActiveUsers = length(userMask) qamRx = zeros(T,nbActiveUsers,nbSymb) tmp = zeros(T,nbSymb) # To be optimized as a view of qamRx for n ∈ eachindex(userMask) - despreading!(tmp,sig,nbUser,c[:,userMask[n]]) + despreading!(tmp,sig,nbUsers,c[:,userMask[n]]) qamRx[n,:] .= tmp end return qamRx end -function despreading!(tmp,sig,nbUser,code) +function despreading!(tmp,sig,nbUsers,code) for n ∈ eachindex(tmp) tmp[n] = 0 - for k ∈ 1 : nbUser - tmp[n] += sig[(n-1)*nbUser + k] * code[k] + for k ∈ 1 : nbUsers + tmp[n] += sig[(n-1)*nbUsers + k] * code[k] end - tmp[n] = tmp[n] / nbUser + tmp[n] = tmp[n] / nbUsers end end + +# Dispatch with StrucCDMA +cdmaSigDecode(sig::AbstractVector,cdma::StrucCDMA) = cdmaSigDecode(sig,cdma.nbUsers,cdma.code,cdma.userMask) diff --git a/src/Waveforms/CDMA/cdmaSigGen.jl b/src/Waveforms/CDMA/cdmaSigGen.jl index da74131..da51682 100644 --- a/src/Waveforms/CDMA/cdmaSigGen.jl +++ b/src/Waveforms/CDMA/cdmaSigGen.jl @@ -1,3 +1,18 @@ +struct StrucCDMA <: Waveform + nbUsers::Int # Number of user + code::Symbol # DSSS code + userMask::Vector{Int} +end + + +""" Instantiate a CDMA structure with a code of size `nbUsers`of type `code`(e.g :ovsf) and active for the user defined in `userMask` +""" +function initCDMA(nbUsers::Number,code::Symbol,userMask::AbstractVector=nothing) + isnothing(userMask) && (userMask=1:nbUsers) + @assert length(userMask) ≤ nbUsers "Number of users should ($(length(nbActiveUser))) be ≤ to the code size ($nbUsers)" + return StrucCDMA(nbUsers,code,userMask) +end + """ Orthogonal Variable Spreading Factor (OVSF) code generation It generates N codes that are orthogonal and follows the OVSF code pattern codes = ovsf(N,initState::UInt8=1) \\ @@ -84,3 +99,8 @@ function _spread_accum!(tmp,seq,code,sF) end end + +# Dispatch with StrucCDMA structure +cdmaSigGen(qamMat::AbstractMatrix,cdma::StrucCDMA) = cdmaSigGen(qamMat,cdma.nbUsers,cdma.code,cdma.userMask) +cdmaSigGen!(sigOut::AbstractVector,qamMat::AbstractMatrix,cdma::StrucCDMA) = cdmaSigGen!(sigOut,qamMat,cdma.N,cdma.code,cdma.userMask) + diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index 32da31b..6573af6 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -166,6 +166,12 @@ end for n = 1 : 1 : N @test sigId[n] == C[n,8] end + # Testing with struct + cdma = initCDMA(N,:ovsf,[8]) + @test cdma isa Waveform + @test cdma isa DigitalComm.StrucCDMA + sigId2 = cdmaSigGen(qamMat,cdma) + @test all(sigId2 .≈ sigId) end @@ -188,4 +194,11 @@ end qamRx = cdmaSigDecode(sigId,N,:ovsf,[n]) @test all(qamRx[1,:] .≈ qamMat[n,:]) end + # Testing with struct + cdma = initCDMA(N,:ovsf,[8]) + @test cdma isa Waveform + @test cdma isa DigitalComm.StrucCDMA + qamRx0 = cdmaSigDecode(sigId,N,:ovsf,[8]) + qamRx1 = cdmaSigDecode(sigId,cdma) + @test all(qamRx0 .≈ qamRx1) end From dbc3d8e99c3b03cb698edbe76a01d8f673a99814 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Wed, 15 Jun 2022 12:18:58 +0200 Subject: [PATCH 7/8] Adding StrucCDMA to genSig/decodeSig --- src/Waveforms/CDMA/cdmaSigGen.jl | 2 +- src/Waveforms/genSig.jl | 10 +- test/test_waveforms.jl | 159 ++++++++++++++++++------------- 3 files changed, 101 insertions(+), 70 deletions(-) diff --git a/src/Waveforms/CDMA/cdmaSigGen.jl b/src/Waveforms/CDMA/cdmaSigGen.jl index da51682..c4743be 100644 --- a/src/Waveforms/CDMA/cdmaSigGen.jl +++ b/src/Waveforms/CDMA/cdmaSigGen.jl @@ -7,7 +7,7 @@ end """ Instantiate a CDMA structure with a code of size `nbUsers`of type `code`(e.g :ovsf) and active for the user defined in `userMask` """ -function initCDMA(nbUsers::Number,code::Symbol,userMask::AbstractVector=nothing) +function initCDMA(nbUsers::Number,code::Symbol,userMask::Union{Nothing,AbstractVector}=nothing) isnothing(userMask) && (userMask=1:nbUsers) @assert length(userMask) ≤ nbUsers "Number of users should ($(length(nbActiveUser))) be ≤ to the code size ($nbUsers)" return StrucCDMA(nbUsers,code,userMask) diff --git a/src/Waveforms/genSig.jl b/src/Waveforms/genSig.jl index b57accf..062527c 100644 --- a/src/Waveforms/genSig.jl +++ b/src/Waveforms/genSig.jl @@ -29,7 +29,9 @@ end function genSig(qamMat,struc::StrucFBMC) return fbmcSigGen(qamMat,struc); end - +function genSig(qamMat,cdma::StrucCDMA) + return cdmaSigGen(qamMat,cdma) +end """ @@ -62,7 +64,9 @@ end function decodeSig(signal,struc::StrucFBMC) return fbmcSigDecode(signal,struc); end - +function decodeSig(signal,struc::StrucCDMA) + return cdmaSigDecode(signal,struc) +end """ --- @@ -89,6 +93,8 @@ function getWaveformName(s::Waveform) name = "FBMC"; elseif typeof(s) == StrucBFOFDM name = "BFOFDM"; + elseif typeof(s) == StrucCDMA + name = "CDMA" end end diff --git a/test/test_waveforms.jl b/test/test_waveforms.jl index 6573af6..f003683 100644 --- a/test/test_waveforms.jl +++ b/test/test_waveforms.jl @@ -25,90 +25,90 @@ nbSubcarriers = length(allocatedSubcarriers); # ---------------------------------------------------- # --- Init OFDM structure ofdm = initOFDM( - nFFT, # --- nFFT : FFT size - 72, # --- nCP : CP size - allocatedSubcarriers # --- allocatedSubcarriers : Subcarrier allocation - ); + 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 - ); + 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 - ); + 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 - ); + 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 - ); + 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 - ); + 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, - ); + scfdma, + ufofdm, + bfofdm, + wola, + fbmc, + ); for (name,struc) in waveforms - @testset "$name" begin - for mcs=[4,16,64,256] - nbBits = nbSymb * nbSubcarriers * Int(log2(mcs)); - # --- Binary sequence - bitSeq = genBitSequence(nbBits); - # Mapping - qamSeq = bitMappingQAM(mcs,bitSeq); - # --- T/F matrix - qamMat = reshape(qamSeq,nbSubcarriers,nbSymb); - # --- Signal - sigId = genSig(qamMat,struc); - # --- Waveform demodulator - qamDec = decodeSig(sigId,struc); - # --- Binary demapper - bitDec = bitDemappingQAM(mcs,qamDec[:]); - # --- BER measure - nbE = sum(xor.(bitDec,bitSeq)); - @test nbE == 0 - end - end + @testset "$name" begin + for mcs=[4,16,64,256] + nbBits = nbSymb * nbSubcarriers * Int(log2(mcs)); + # --- Binary sequence + bitSeq = genBitSequence(nbBits); + # Mapping + qamSeq = bitMappingQAM(mcs,bitSeq); + # --- T/F matrix + qamMat = reshape(qamSeq,nbSubcarriers,nbSymb); + # --- Signal + sigId = genSig(qamMat,struc); + # --- Waveform demodulator + qamDec = decodeSig(sigId,struc); + # --- Binary demapper + bitDec = bitDemappingQAM(mcs,qamDec[:]); + # --- BER measure + nbE = sum(xor.(bitDec,bitSeq)); + @test nbE == 0 + end + end end @@ -180,7 +180,7 @@ end N = 16 mcs = 4 nbSymb = nS ÷ N ÷ Int(log2(mcs)) - # Mapping + # Mapping bitSeq = genBitSequence(nS); qamSeq = bitMappingQAM(mcs,bitSeq); # --- T/F matrix @@ -202,3 +202,28 @@ end qamRx1 = cdmaSigDecode(sigId,cdma) @test all(qamRx0 .≈ qamRx1) end + + +@testset "CDMA through genSig/decodeSig" begin + nbSymb = 14 + nbUsers = 16 + cdma = initCDMA(nbUsers,:ovsf) + for mcs=[4,16,64,256] + nbBits = nbSymb * nbUsers * Int(log2(mcs)); + # --- Binary sequence + bitSeq = genBitSequence(nbBits); + # Mapping + qamSeq = bitMappingQAM(mcs,bitSeq); + # --- T/F matrix + qamMat = reshape(qamSeq,nbUsers,nbSymb); + # --- Signal + sigId = genSig(qamMat,cdma); + # --- Waveform demodulator + qamDec = decodeSig(sigId,cdma); + # --- Binary demapper + bitDec = bitDemappingQAM(mcs,qamDec[:]); + # --- BER measure + nbE = sum(xor.(bitDec,bitSeq)); + @test nbE == 0 + end +end From 2e81d1ae33557df99e1882bf1607dce7d2a933e0 Mon Sep 17 00:00:00 2001 From: Robin Gerzaguet Date: Wed, 29 Jun 2022 12:08:51 +0200 Subject: [PATCH 8/8] Adding BER example in CDMA Noter that as we use SNR, performance are the same for each code size. To see the benefits of spreading, the noise level should be fixed (and not the SNR). --- examples/example_BER_CDMA.jl | 151 +++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 examples/example_BER_CDMA.jl diff --git a/examples/example_BER_CDMA.jl b/examples/example_BER_CDMA.jl new file mode 100644 index 0000000..4370cc9 --- /dev/null +++ b/examples/example_BER_CDMA.jl @@ -0,0 +1,151 @@ +module example_BER_cdma + + + +# ---------------------------------------------------- +# --- Modules +# ---------------------------------------------------- +using DigitalComm +# --- External Modules +using Plots +using Printf +using FFTW +using Statistics + + + +# ---------------------------------------------------- +# --- Core functions +# ---------------------------------------------------- +function getBER(waveform,mcs,snrVect,nbSymb,nbIt) + if waveform isa DigitalComm.StrucCDMA + # --- Get code allocation + nbSubcarriers = length(waveform.userMask) + else + # --- Getting frequency allocation + allocatedSubcarriers = waveform.allocatedSubcarriers; + nbSubcarriers = length(allocatedSubcarriers); + end + # Deduce number of required bits + nbBits = nbSymb * nbSubcarriers * Int(log2(mcs)); + # --- Init BER vector + nbSNR = length(snrVect); + ber = zeros(Float64,nbSNR); + # --- Iterative PSD calculation + for k = 1 : 1 : nbSNR + # --- Update counters + nbC = 0; + nbE = 0; + powSig = 0; + for iN = 1 : 1 : nbIt + # --- Binary sequence + bitSeq = genBitSequence(nbBits); + # Mapping + qamSeq = bitMappingQAM(mcs,bitSeq); + # --- T/F matrix + qamMat = reshape(qamSeq,nbSubcarriers,nbSymb); + # --- Signal + sigId = genSig(qamMat,waveform); + # ---------------------------------------------------- + # --- Channel + # ---------------------------------------------------- + # --- AWGN + if iN == 1 + # We compute the power based on generated signal + # We troncate the beginning and end of signal to avoid + # estimation biais induced by (potential) filter tails + powSig = mean(abs2.( @views sigId[1+ end÷4 : end - end÷4])); + end + sigNoise, = addNoise(sigId,snrVect[k],powSig); + # ---------------------------------------------------- + # --- Rx Stage + # ---------------------------------------------------- + # --- Waveform demodulator + qamDec = decodeSig(sigNoise,waveform); + # --- Binary demapper + bitDec = bitDemappingQAM(mcs,qamDec[:]); + # --- BER measure + nbE += sum(xor.(bitDec,bitSeq)); + nbC += length(bitSeq); + end + # --- BER measure + # Adding 1e-10 to avoid log plot of zero errors + ber[k] = 1e-10 .+ nbE / nbC; + end + return ber +end + +# ---------------------------------------------------- +# --- Main routine +# ---------------------------------------------------- +function main() + # ---------------------------------------------------- + # --- Overall parameters + # ---------------------------------------------------- + # --- Overall PHY parameters + nbIt = 20; # --- Iteration number + nbSymb = 1400; # --- Number of symbols (one frame) + nFFT = 1024; # --- Base FFT size + samplingFreq = 3.84e6; # --- Frequency value (MHz) + mcs = 16; # --- 16-QAM + snrVect = (-10:30); + # --- Frequency allocation + allocatedSubcarriers= getLTEAlloc(nFFT); + + # ---------------------------------------------------- + # --- Waveform contender + # ---------------------------------------------------- + # --- Init OFDM structure + cdma4 = initCDMA( + 4, + :ovsf + ) + cdma16 = initCDMA( + 16, + :ovsf + ) + cdma32 = initCDMA( + 32, + :ovsf + ) + cdma64 = initCDMA( + 64, + :ovsf + ) + # ---------------------------------------------------- + # --- Merging structures + # ---------------------------------------------------- + # Create a dictionnary to rule them all + waveforms = initWaveforms( + cdma4, + cdma16, + cdma32, + cdma64, + ); + + + # ---------------------------------------------------- + # --- BER main calculation + # ---------------------------------------------------- + # --- Init plot container + plt = plot(reuse=false,yscale=:log10,legend=:bottomleft); + # --- Iterative PSD generation + for (name,struc) in waveforms + # --- Calculate PSD for the configuration + ber = getBER(struc,mcs,snrVect,nbSymb,nbIt); + # --- Plot stuff + plot!(plt,snrVect,ber,label=struc.nbUsers); + end + # --- Adding metata do plot curve + xlabel!("SNR [dB]"); + ylabel!("Bit Error Rate"); + ylims!(1e-6,1); + display(plt); +end + + + +end + + +