BER-vs-Eb/N0 for quadrature-amplitude modulation on the two-node COSMOS framework. QAM packs more
bits per symbol than PSK (4/6/8 vs 1/2/3) by using both amplitude and phase — at the cost of needing
more Eb/N0 for the same BER. This is the second modulation family of the
Two-Node Digital-Modulation BER framework.
Quadrature-amplitude modulation places symbols on a square grid: 16-QAM (4 bits/symbol),
64-QAM (6), 256-QAM (8). Unlike PSK, QAM is not constant-modulus — the symbols have
several amplitude levels — which makes it more spectrally efficient but more sensitive to noise and,
importantly, to carrier recovery.
The framework reuses the same pipeline as PSK:
RRC-shaped TX, and on RX an AGC → FLL → RRC matched filter → Gardner symbol sync, then a data-aided
BER against a known PN reference. One difference matters: the M-PSK Costas loop cannot lock QAM
(it assumes a constant modulus), so the QAM receiver drops the Costas loop and recovers the residual
carrier phase with a data-aided trained MMSE equalizer post-capture.
Read the shared framework overview first for the model,
the three domains, and the two sweep methods. This page covers what is specific to QAM.
Source (clone & run): https://gitlab.orbit-lab.org/tutorials/ber
After completing this tutorial you will be able to:
| Difficulty | Advanced |
| Estimated time | 45 min |
| Domain / sandbox | any two-B210 domain (cabled link recommended for higher-order QAM) |
| Topic group | SDR & GNU Radio |
| Last verified | 2026-07-21 (real 16-QAM measured on sb4 JFW matrix, node1-4→node1-5 — see Verification) |
Background knowledge
Account & access
Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
nodes[0] |
transmitter | 1 | USRP B210, selected first in OMF_NODES |
nodes[1] |
receiver | 1 | USRP B210, selected second in OMF_NODES |
Disk images
| Image | Load onto | Provides |
|---|---|---|
ubuntu2404-uhd4.9-gr3.10.ndz |
both nodes | UHD 4.9 + GNU Radio 3.10 preinstalled |
Software components
| Component | Version | Source |
|---|---|---|
| UHD | 4.9 | preinstalled in image |
| GNU Radio | 3.10 | preinstalled in image (uses gnuradio.digital.qam.qam_constellation) |
| cosmos-ansible bundle | — | git clone https://gitlab.orbit-lab.org/tutorials/ber on the console |
Spectrum / RF / special
Higher-order QAM needs a high, clean Eb/N0. A cabled/conducted link (with a fixed pad) or the
sb4 attenuator matrix gives the cleanest constellation; open-air links suit 16-QAM better than
64/256-QAM.
Two B210 nodes in one domain, driven from the domain console. cosmos-ansible starts the QAM
transmitter on nodes[0] and the capture/measure pipeline on nodes[1]; the RF path (open-air,
cabled, or through the attenuator matrix) is per the
framework topology table.
ssh <username>@console.<domain>.cosmos-lab.org
omf load -i ubuntu2404-uhd4.9-gr3.10.ndz -t <node0>,<node1>
omf tell -a on -t <node0>,<node1>
git clone https://gitlab.orbit-lab.org/tutorials/ber.git
cd ber/ansible
OMF_NODES="<node0>,<node1>" ansible-playbook ber/ber.yml \
-e '{"ber_mods":["16qam"],"ber_rx_gain":60,"ber_nsyms":2000000}'
The QAM receiver uses no Costas loop; it recovers the carrier phase with the data-aided trained
equalizer (--equalizer trained, the QAM default) and counts BER against the known PN reference.
OMF_NODES="<node0>,<node1>" ansible-playbook ber/ber.yml \
-e '{"ber_mods":["64qam"],"ber_rx_gain":60,"ber_nsyms":2000000}'
256-QAM needs a very clean, high-Eb/N0 link — run it on a cabled domain.
ansible-playbook ber/plot.yml # -> results/ber_qam.svg
sdr_plot.py carries the square-QAM theory curves for 16/64/256-QAM and overlays your measured
points.
Measured on the sb4 JFW attenuator matrix — node1-4 (B205mini s/n 30F554A) → matrix (0 dB) →
node1-5 (s/n 30F5560), 2.4 GHz. The conducted, calibrated link avoids the multipath that destroys
single-carrier QAM over the air, giving a clean real-link Eb/N0 of ~15.6 dB. QAM is not
constant-modulus, so the receiver recovers the carrier data-aided against the known PN reference; the
BER-vs-Eb/N0 curve is then traced by AWGN injection on that capture (6 M symbols = 24 M
bits/point, every 0.4 dB):
| Eb/N0 (dB) | 4 | 6 | 8 | 10 | 12 | 14 | 15.6 |
|---|---|---|---|---|---|---|---|
| 16-QAM measured | 6.42e-2 | 3.41e-2 | 1.44e-2 | 4.56e-3 | 1.03e-3 | 1.72e-4 | 3.5e-5 |
| 16-QAM theory | 5.86e-2 | 2.79e-2 | 9.25e-3 | 1.75e-3 | 1.39e-4 | 2.76e-6 | — |
Theory vs real radios — what the graph shows. The measured curve rides theory at low Eb/N0 and
pulls above it as SNR rises: ~0.5 dB loss at 4 dB widening to several dB by 14 dB. That growing gap
is the B210 EVM floor — QAM's dense constellation is far more sensitive to the radio's residual
phase noise / amplitude imperfection than PSK (compare the PSK page's ~1–2 dB, roughly constant). It is
the honest cost of a real transmitter and receiver over a calibrated, conducted channel.
64-QAM / 256-QAM need a higher-SNR link. 64-QAM's waterfall lives at ~14–20 dB and 256-QAM at
~24 dB — above the ~15.6 dB the 0-dB matrix link and B210 EVM reach. A clean deep 64/256-QAM
curve needs the sb4 JFW attenuator matrix (calibrated conducted link); 16-QAM is the
representative real curve here. On the grid open-air link single-carrier QAM does not lock at
all — its dense constellation is destroyed by multipath (real-link Eb/N0 ≈ −6 dB) even with a shared
OctoClock; that failure is the motivation for OFDM.
omf tell -a offh -t <node0>,<node1>
Release the reservation from the portal when finished.
| Symptom | Likely cause | Fix |
|---|---|---|
| QAM capture won't lock (real-link Eb/N0 very low, BER ≈ 0.5) | dense QAM constellation + residual carrier/timing; no live Costas for QAM | use a cabled/clean link; higher ber_rx_gain; (follow-up: decision-directed QAM receiver) |
| Curve floors early | too few bits for the low-BER tail | raise ber_nsyms |
| 256-QAM never resolves | link Eb/N0 below what 256-QAM needs (~24 dB for 1e-3) | run on a cabled domain with a strong, clean link |
No UHD Devices Found |
node ↔ B210 USB not enumerated | power-cycle the node; check uhd_find_devices |
Author(s): COSMOS team · Last verified: 2026-07-21 (real 16-QAM measured on sb4 JFW attenuator matrix, node1-4/node1-5 s/n 30F554A+30F5560, 2.4 GHz, AWGN-injection, 24 M bits/pt; 64/256-QAM need a higher-SNR link than the matrix reaches) · Tested image/release: ubuntu2404-uhd4.9-gr3.10.ndz / GNU Radio 3.10.12 · Tags: sdr, gnuradio, ber, qam, 16qam, 64qam, 256qam, sigmf