Simulation of a CDMA system with QPSK modulation
In modern wireless communication systems, the technology of multiple access with code division of channels CDMA (Code Division Multiple Access) remains the fundamental method of providing simultaneous data transmission for multiple users in a common frequency band. A key aspect of designing such systems is ensuring channel orthogonality and noise tolerance, which requires careful modeling during the development phase.
Key principles of CDMA:
| The principle | Description |
|---|---|
| Spectrum expansion | Each bit of data is multiplied by a high-frequency code sequence (chip sequence), which "expands" the signal in frequency |
| Orthogonality of codes | The codes of different channels are selected so that their cross-correlation is close to zero (for example, Walsh codes), which minimizes interference |
| Frequency sharing | All users work on the same carrier frequency at the same time, differing only in the code |
| Binding (Despreading) | On the receiving side, the signal is multiplied by the same code, which restores the original data and suppresses the signals of other users |
Advantages of CDMA:
- Resistance to narrowband interference and fading
- The possibility of soft handover (soft handoff) in cellular networks
- Flexible system capacity management
- Increased transmission security due to pseudorandom codes
Application:
- 2G/3G cellular networks (IS-95, CDMA2000, WCDMA)
- Global positioning systems (GPS, GLONASS)
- Satellite communications
- Military secure communication systems
In our model, CDMA is implemented through a combination of QPSK modulation, Walsh codes (to separate the pilot and user channels) and PN sequences (for additional spectrum expansion), which corresponds to the classical architecture of direct spectrum expansion systems (DSSS-CDMA).
The model consists of two user subsystems (Pilot_Channel and User1_Traffic_Channel), which generate pilot channel and user traffic signals. The signals are subjected to quadrature phase manipulation (QPSK), expanded with unique code sequences, and summed before transmission through a noise channel. On the receiver side, a reverse convolution and demodulation operation is performed to restore the original data.
Integration with Engee allows flexible control of signal parameters: code sequences calculated in the script are loaded into the model via Signal From Workspace blocks, which ensures high accuracy of matching the mathematical description and simulation.
Pkg.add("JLD2")
using JLD2, Statistics, DataFrames
The code below performs the initial loading and visual validation of the simulation data.
A = load("data.jld2", "A")
Size_A = length(A)
println("Size A: $(Size_A)")
plot(A[1:300])
Function pn_gen(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
- Implements a pseudorandom sequence generator (PN) based on a linear feedback shift register (LFSR).
- Accepts the initial state of the register, the vector of exponents of the generating polynomial, and the required length of the output sequence.
- Determines the length of register N and creates a copy of it for operation.
- Converts the exponents of the polynomial into taps: if the exponent is 0, it adds a tap to the last position N, otherwise it adds a tap to position N—e.
- Generates a sequence of a given length: at each step, it stores the last bit of the register as the output, calculates the feedback bit as the XOR of all taps, and shifts the register to the right with the feedback bit at the beginning.
- Returns an array of integers 0 and 1 (compatible with the MATLAB format).
Function pn_gen_walsh(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
- Causes
pn_gento obtain the basic binary sequence. - Performs format conversion: each 0 is replaced by 1, each 1 is replaced by -1.
- Returns a sequence in the +1/-1 format, ready for use in modulation and multiplication by Walsh codes.
Function walsh_hadamard(n::Int, size::Int)
- Generates orthogonal Walsh-Hadamard codes of a given length.
- Starts with a basic 4×4 Hadamard matrix.
- Performs two iterative extensions according to the [H H; H -H] rule, forming a 16×16 matrix.
- Extracts a string with the index n+1 (numbering channels from 0).
- Repeats (repeats) the received string the required number of times to achieve the required length size.
- Returns a sequence of +1/-1 orthogonal to the other rows of the Hadamard matrix.
function pn_gen(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
N = length(init_state)
reg = copy(init_state)
taps = Int[]
for e in poly_exp
if e == 0
push!(taps, N)
elseif e != N
push!(taps, N - e)
end
end
outputs = Bool[]
for _ in 1:n
push!(outputs, reg[end] == 1)
fb = 0
for t in taps
fb = xor(fb, reg[t])
end
reg = [fb; reg[1:end-1]]
end
return Int.(outputs)
end
function pn_gen_walsh(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
raw = pn_gen(init_state, poly_exp, n)
return [x == 0 ? 1 : -1 for x in raw]
end
function walsh_hadamard(n::Int, size::Int)
H = [1 1 1 1; 1 -1 1 -1; 1 1 -1 -1; 1 -1 -1 1]
for i in 1:2
H = [H H; H -H]
end
code = H[n+1, :]
return repeat(code, inner=div(size, 16))
end
The code for generating PN codes is shown below.
The PN code is generated separately for two branches of the signal — common-mode (I) and quadrature (Q). For each branch, a special generator is used that generates a sequence of random values (+1 or -1) based on certain parameters of the shift register and the feedback polynomial.
Walsh codes are created to separate channels within the system. The pilot channel receives a one—unit code, and the first user channel receives an alternating code. These codes allow you to separate the signals of different users and help the receiver to identify the desired signal among the many transmitted simultaneously.
For each channel, the final signal is generated by multiplying the PN code and the corresponding Walsh code. This ensures the separation of signals from different users and helps to efficiently transfer data in parallel. Then the characteristics of the received signals are checked. It is confirmed that they are well balanced (the average is close to zero, the standard deviation is 1), which is important for high-quality data transmission.
pn_I = pn_gen_walsh([1; zeros(Int, 14)], [15, 13, 9, 8, 7, 5, 1], Size_A)
pn_Q = pn_gen_walsh([1; zeros(Int, 14)], [15, 12, 11, 10, 6, 5, 4, 3, 1], Size_A)
walsh_pilot = walsh_hadamard(0, Size_A) # [1,1,1,1...]
walsh_user1 = walsh_hadamard(1, Size_A) # [1,-1,1,-1...]
pilot_I = pn_I .* walsh_pilot
pilot_Q = pn_Q .* walsh_pilot
user1_I = pn_I .* walsh_user1
user1_Q = pn_Q .* walsh_user1
code_list = [A, pilot_I, pilot_Q, user1_I, user1_Q]
code_names = ["A (engee)", "Pilot I", "Pilot Q", "User1 I", "User1 Q"]
df_stats = DataFrame(
Code = code_names,
Length = [length(c) for c in code_list],
Average = [round(mean(c), digits=4) for c in code_list],
Std = [round(std(c), digits=4) for c in code_list],
Min = [minimum(c) for c in code_list],
Max = [maximum(c) for c in code_list],
Percent_1 = [round(count(==(1), c)/length(c)*100, digits=2) for c in code_list],
Percent_minis1 = [round(count(==(-1), c)/length(c)*100, digits=2) for c in code_list]
)
println(df_stats)
The analysis of the statistical table shows the following characteristics of the signals:
- The original signal "A" is unipolar with an equal distribution of zeros and ones. The standard deviation corresponds to expectations for a binary signal with an equal probability of symbols.
- The pilot channel signals (Pilot I and Pilot Q) have average values close to zero, which indicates good balancing and the absence of a constant component. The standard deviation is equal to one, confirming the normalized signal power. The percentage distribution of units and minus units is close to 50/50, ensuring balanced sequences.
- The user channels (User1 I and User1 Q) also show near-zero averages and a standard deviation of one. The distribution of positive and negative units is as balanced as possible, especially for User1 Q.
The general conclusions confirm that all channel signals have optimal transmission characteristics: zero constant component, normalized power and equal probability of symbols. Small deviations from the ideal are associated with the finiteness of the sequence and statistical fluctuations.
The code below calculates the correlation between pairs of channels, it creates a list corr_data, containing pairs of channels and corresponding correlation coefficients. The coefficient is calculated by element-wise multiplying the corresponding elements of the two channels, summing the result and normalizing by dividing by the length of the sequence. Then the table is formed df_corr with two columns: names of channel pairs and numerical values of correlations. The table is displayed on the screen with the command println.
corr_data = [
("Pilot I × User1 I", round(sum(pilot_I .* user1_I)/length(pilot_I), digits=4)),
("Pilot Q × User1 Q", round(sum(pilot_Q .* user1_Q)/length(pilot_Q), digits=4))
]
df_corr = DataFrame(
Pair = [x[1] for x in corr_data],
Correlation = [x[2] for x in corr_data]
)
println(df_corr)
The zero correlation confirms the orthogonality of the Walsh codes, the absence of mutual interference and the correct formation of channel signals in the CDMA system. Now let's run our model and make sure that the selected values are correct.
function run_model( name_model)
Path = (@__DIR__) * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()]
model = engee.open( name_model )
model_output = engee.run( model, verbose=true );
else
model = engee.load( Path, force=true )
model_output = engee.run( model, verbose=true );
engee.close( name_model, force=true );
end
sleep(0.1)
return model_output
end
run_model("CDMA_MAI")
BER = collect(simout["CDMA_MAI/Error Rate Calculation.1"]).value[end]
println("Bit Error rate (BER): $(BER[1])")
println("Number of errors: $(BER[2])")
println("Total number of bits: $(BER[3])")
Conclusion
At the signal-to-noise ratio SNR = 10 dB, zero bit error probability was achieved (BER = 0). This demonstrates the correct operation of all components of the CDMA model: generation of pseudo-noise sequences, creation of orthogonal channel codes, QPSK modulation with phase shift. /4 and subsequent demodulation.
The obtained result of the correlation analysis confirms the complete absence of crosstalk between channels due to the perfect orthogonality of the Walsh codes used. Thus, the simulation successfully demonstrated the correct functioning of the CDMA system and its ability to provide reliable lossless data transmission over a given sequence length.