Python implementation devoted to the comparative study of MIMO equalisers and to the cancellation of a coloured interference using a Kalman filter.
The project covers the following parts:
- Comparison of the ZF, MMSE and sequential detector (Cholesky-
based) equalisers on the diagonal channel
H_1 = diag(0.1, 1, 10)with BPSK and 4-PAM modulations. - Sweep of the noise variance
σ_g²on the complex square channelH_2, comparing the three equalisers with BPSK. - Same sweep on the rectangular channel
H_3(2 × 3) and on its row-swapped versionH̃_3, comparing MMSE against the sequential detector. - Cancellation of a coloured interference generated by a linear hidden-state
system, combining a Kalman filter, the Viterbi decoder from Lab 2 and an
ASCII converter to recover a hidden message stored in
data_rx.txt.
.
├── .gitignore
└── src/
├── main.py # Entry point (runs every part)
├── part1.py # Part 1: diagonal H_1, BPSK and 4-PAM
├── part2.py # Part 2: H_2, sweep over σ_g²
├── part3.py # Part 3: H_3 and H̃_3
├── part4.py # Part 4: interference cancellation
├── equalizers.py # ZF, MMSE and sequential detector
├── modulation.py # Constellations, slicers and channel simulation
├── interf_cancel.py # Kalman filter and Part 4 pipeline
├── convert2char.py # Bits → ASCII (Lab 2, unchanged)
├── viterbi_decode_101_111.py # Viterbi decoder (Lab 2, unchanged)
└── data_rx.txt # Received samples for Part 4
- Python ≥ 3.9
numpymatplotlib(only required for the figures of parts 1, 2 and 3)
No further dependencies are used.
git clone <repository-url>
cd <repository-name>
pip install numpy matplotlibThere is no build step; the project runs directly with the Python interpreter.
All commands are run from the src/ folder:
cd srcpython main.pyParts 1 → 4 are executed sequentially. Each part prints its own result table
to standard output and, when applicable, writes a .png figure to the
current working directory.
python main.py --only 1 2 # only Part 1 and Part 2
python main.py --skip 4 # every part except Part 4By default, Part 4 looks for data_rx.txt next to main.py. To use a
different path:
python main.py --data_rx /path/to/data_rx.txtIf the file does not exist, Part 4 is skipped with a warning and the remaining parts run normally.
Every partN.py is also executable on its own:
python part1.py
python part2.py
python part3.py
python part4.py # equivalent to: python part4.py data_rx.txtFor BPSK and 4-PAM, runs a single simulation of K = 10 000 transmitted
vectors with σ_g² = 1 and compares the SER obtained by the three
equalisers. The sequential detector uses U = H_1 directly because H_1 is
diagonal, real and positive.
Output: SER table per equaliser and per modulation, plus the figure
part1_SER_comparison.png (grouped bar chart).
Sweeps σ_g² over a grid of 30 points in [10⁻³, 1] and plots the BER on
a semilogarithmic scale for the three equalisers.
Output: BER table versus σ_g² and the figure part2_H2_BPSK.png.
Runs the same sweep on H_3 (2 × 3) and on H̃_3, its row-swapped
version, comparing MMSE against the sequential detector. The contrast
between the two plots illustrates the effect of the row permutation on the
successive cancellation (error propagation).
Output: BER table and the two-panel figure part3_H3_BPSK.png.
Loads data_rx.txt, which contains the observations
r[k] = Bᵀ (s[k] + x[k]) of a system in which x[k] is a coloured
interference generated by
x[k+1] = A x[k] + u[k], u[k] ~ N(0, σ_u² I), σ_u = 0.15,
with A, Bᵀ and σ_u provided by the assignment. The signal s[k]
contains the two BPSK bits of information bit k, encoded with the
convolutional code G(D) = [D² + 1, D² + D + 1].
The implemented pipeline is:
- Pre-whitening:
z[k] = (Bᵀ)⁻¹ r[k] = s[k] + x[k]. - Interference estimation with the Kalman filter from Lab 1, used
unchanged, with
B = Iandσ_v = 1(the BPSK symbols play the role of the observation noise, with covarianceI_2). - Cancellation:
ŝ[k] = z[k] − x̂[k | k]followed by a BPSK hard decision. - Interleaving of the two coded bit streams and Viterbi decoding with the script from Lab 2.
- Removal of the two tail bits of the convolutional encoder (
K − 1 = 2) and ASCII conversion of the resulting bits.
Output: the decoded message is printed to standard output.
After running python main.py from src/, the following files appear in
the current working directory:
| File | Part | Description |
|---|---|---|
part1_SER_comparison.png |
1 | Grouped SER bar chart (BPSK vs 4-PAM, three equalisers) |
part2_H2_BPSK.png |
2 | BER vs σ_g² for BPSK over H_2 |
part3_H3_BPSK.png |
3 | BER vs σ_g² for BPSK over H_3 and H̃_3 |
The numerical tables of every part and the Part 4 message are printed to standard output.
Every simulation is seeded with np.random.default_rng(20251107), so the
generated tables and figures are bit-for-bit reproducible. Other relevant
parameters, fixed in each module:
K_SYMBOLS = 10_000— size of the transmitted-symbol block.SIGMA_G2 = 1.0— noise variance for Part 1.SIGMA_G2_GRID = np.linspace(0.001, 1.0, 30)— grid for Parts 2 and 3.