Crawl → walk → run through IQ analysis on six pre-recorded captures. No radio hardware required: the recordings ship with the repo. Start by finding one steady carrier; finish by pulling a hopping intruder out of a jammed, contested band.
git clone → pip install -r requirements.txt → python3 tools/iqlab.py …. Or
just look at the spectrograms below. Every signal is synthetic; nothing here was
received off the air.
Part of the Marpoint Station universe. Coldferry is the RF/EW range.
Six SigMF recordings (.sigmf-data + .sigmf-meta), 2.048 Msps, complex
float32. The metadata carries annotations marking every signal in time and
frequency, so each file is also its own answer key, and opens cleanly in
inspectrum / URH / any SigMF-aware tool.
| # | Recording | Phase / level | What it teaches |
|---|---|---|---|
| 1 | cap01_cw_carrier |
Listen · crawl | IQ basics; find a carrier, measure its offset |
| 2 | cap02_ook_beacon |
Listen · crawl | Read a waterfall in time; on/off keying |
| 3 | cap03_fsk_telemetry |
Locate · walk | Recognize 2-FSK; measure shift + symbol rate |
| 4 | cap04_lfm_chirp |
Locate · walk | Identify a chirp; measure span, period, direction |
| 5 | cap05_fhss_hopper |
The Intruder · run | Detect a hopper; recover dwell + channel set |
| 6 | cap06_contested_scene |
The Intruder · run | Capstone: separate benign from threat under jamming |
Load-bearing lesson: energy in the band is not the same as a signal you understand, and a signal you understand is not the same as a threat. Prove each step; don't skip to the last one.
cap05 · a frequency hopper (run):

cap06 · the contested capstone, annotations overlaid (the answer key):

All six render in docs/spectrograms/.
tools/iqlab.py loads a SigMF recording and shows it four ways:
python3 tools/iqlab.py captures/cap01_cw_carrier --psd
python3 tools/iqlab.py captures/cap05_fhss_hopper --waterfall
python3 tools/iqlab.py captures/cap03_fsk_telemetry --time --waterfall
python3 tools/iqlab.py captures/cap06_contested_scene --annotate # reveal the key--time (I/Q + magnitude) · --psd (where the energy is) · --waterfall
(energy in time and frequency) · --annotate (overlay the SigMF answer key) ·
--save out.png.
Then work the lab guide, station by station.
Prefer real tools? The .sigmf-data files are plain cf32 (GNU Radio complex
float). Load them directly in inspectrum, Universal Radio Hacker (URH),
gqrx (file source), or a GNU Radio flowgraph. Sample rate and center are in
the .sigmf-meta.
captures/make_captures.py builds every recording deterministically (fixed seed)
and writes the SigMF metadata + annotations. Change a modulation, add a channel,
bury the intruder deeper: the generator is the construction reference. Re-run:
python3 captures/make_captures.pyColdferry's full four-phase arc is Listen → Locate → The Intruder → Effects
(electronic support → attack → protection). The first three ship as runnable IQ
analysis. Effects (actually transmitting jamming or spoofing) does not ship
as a runnable exercise, because transmitting across 1 MHz-1 GHz needs hardware
and puts you in regulated spectrum. Instead, the capstone (cap06) contains a
barrage-jamming burst so you can see and characterize an electronic attack
from the receive side, and the guide treats protection as an analysis-and-design
discussion. Live TX belongs on a range under authority. See the hardware
appendix in the guide.
Bring your own SDR (optional). Everything runs with zero hardware, but a ~$30 RTL-SDR lets you capture your own signals into the same SigMF format and analyze them with the same toolkit.
Waxhiss teaches you to read wired traffic; Coldferry moves the same prove-vs-infer discipline into the RF domain, where the "packets" are just energy in a band and you have to earn every conclusion. It's the widest-aperture range in the set: detection before attribution, attribution before action.
Authorized training environment only. All signals are synthetic; the scenario is fictional. The runnable content is receive-side analysis: it transmits nothing. See DISCLAIMER.md. Licensed under MIT.

