slop-stuff cheatsheets & experiments git

slop-stuff / Embedded & hardware

SDR

Listen to the spectrum.

Software-defined radio: IQ, sampling, DSP, GNU Radio, and common signals.

RFIQFFTGNU Radio

Software-defined radio turns a cheap USB dongle into a receiver for everything from FM broadcast to ADS-B and weather satellites. After the ADC, it’s all just math.

Quick reference

The commands and numbers you reach for most — the fast path into every section below.

Capture IQ

rtl_sdr -f 100e6 -s 2.4e6 -g 20 out.bin
# 2.4 MS/s ≈ 2.4 MHz of spectrum

Live FM

rtl_fm -f 100.1e6 -M wbfm \
  -s 200k -r 48k - | aplay -r 48k -f S16_LE

IQ sample

I = A·cos(2π·Δf·t)
Q = A·sin(2π·Δf·t)
mag   = hypot(I, Q)    # strength
phase = atan2(Q, I)    # angle

Rate ↔ bandwidth

fs ≈ capture bandwidth
fs > 2 × BW        # Nyquist
# FM ≈ 200 kHz → 480 kS/s+

FFT spectrum

# scan 88–108 MHz, log power
rtl_power -f 88e6:108e6:100k \
  -g 30 -e 1m scan.csv

Flowgraph

RTL-SDR Source → Low Pass →
  WBFM Receive → Audio Sink
# wire in GRC, run the .py

Signals at a glance

FM       88–108 MHz   wbfm
ADS-B    1090 MHz     dump1090
NOAA APT 137 MHz      noaa-apt
AIS      162 MHz      GMSK

ADS-B decode

rtl_sdr -f 1090e6 -s 2.4e6 -g 10 - | \
  dump1090 --interactive

REF: Everything here is expanded below. Capture flags in Tools, IQ math in IQ, bandwidth in Frequency, FFT in DSP, flowgraphs in GNU Radio, and the signal table in Signals.

What SDR is

A software-defined radio replaces analog mixers and filters with an ADC and DSP, so the same dongle receives anything from AM broadcast to ADS-B.

1. ADC + tuner

The tuner shifts an RF band down to an intermediate frequency; the ADC digitizes it. Everything after the ADC is software.

2. Sampling theorem

To represent a signal without aliasing, sample at more than twice its bandwidth: fs > 2 × BW.

3. Front-end chain

Antenna → LNA → tuner/mixer → ADC → sample buffer → host over USB.

4. Hardware

RTL-SDR Blog V4 (RX, 500 kHz–1.766 GHz, ~2.4 MS/s), HackRF (RX/TX, 1 MHz–6 GHz), SDRplay / Airspy (wide dynamic range).

RF: Receive-only first. RTL-SDR dongles are cheap, legal to own, and enough to learn sampling, IQ, and DSP. Only HackRF-class devices can transmit — and transmitting needs a license.

IQ & complex signals

Each sample is a complex number I + jQ: two real streams that carry both amplitude and phase, so the spectrum on both sides of the center frequency survives.

RF in (real signal) → Mixer × 2 (cos & sin LO) → I, Q (two baseband streams) → I + jQ (complex samples) → DSP (filter · FFT · demod)

I and Q

I multiplies the signal by the local oscillator; Q multiplies by the same LO shifted 90°. The phase between them preserves the sign of the frequency offset.

# one IQ sample = a complex pair
# I = A·cos(2π·Δf·t)
# Q = A·sin(2π·Δf·t)
sample = I + jQ

Negative frequencies

A real signal is mirrored around 0 Hz; a complex (IQ) signal is not. The sign of the frequency tells you whether a carrier is above or below the tuned center.

+1 MHz in IQ  → carrier above center
-1 MHz in IQ  → carrier below center

j: Why IQ? It’s the difference between hearing a station on either side of your dial. With only real samples you can’t tell +100 kHz from -100 kHz; IQ keeps them distinct so filters and demodulators work.

Complex numbers in one minute

A complex sample packs two reals into one value. Magnitude is signal strength; angle is phase. The conjugate flips the spectrum’s sign.

magnitude = sqrt(I² + Q²)   # signal strength
phase     = atan2(Q, I)     # angle, in radians
conjugate = I - jQ          # mirrors the spectrum

Frequency & bandwidth

Frequency is where on the dial you listen; bandwidth is how wide a slice you capture. Sample rate is the number of IQ pairs per second.

1 GHz = 1000 MHz = 10⁶ kHz = 10⁹ Hz

UnitScaleTypical signal
Hzcycles per secondbaseband audio, POCSAG bit rate
kHz10³ HzAM channel (~10 kHz), RTTY
MHz10⁶ HzFM broadcast (88–108), NOAA APT 137, ADS-B 1090
GHz10⁹ HzWi-Fi 2.4/5, GPS 1.575

Sample rate vs bandwidth

Sample rate fs sets the capture bandwidth: an RTL-SDR at 2.4 MS/s sees roughly 2.4 MHz of spectrum at once.

rtl_sdr -f 100e6 -s 2.4e6 out.bin

Nyquist limit

To avoid aliasing you must sample at fs > 2 × BW. A 200 kHz FM signal needs at least 400 kS/s — 480 kS/s is comfortable.

# FM broadcast ≈ 200 kHz wide
rtl_fm -f 100e6 -M fm -s 200k -r 48k -

Decimation & gain

Decimation drops every Nth sample (after filtering) to lower the rate; interpolation inserts zeros to raise it. Gain boosts signal before the ADC — too much clips.

# capture with manual gain
rtl_sdr -f 100e6 -s 2.4e6 -g 20 out.bin

DSP building blocks

The same handful of operations appear in every receiver, in roughly this order.

  1. Converts time-domain IQ samples into frequency bins — the spectrum behind the waterfall display.
  2. A finite impulse response filter that passes a band and rejects the rest: low-pass, band-pass, or channel-select.
  3. Multiply samples by a Hann or Hamming window before the FFT to reduce spectral leakage from edge discontinuities.
  4. Envelope detector: the magnitude of each sample, |I + jQ|, then a low-pass filter.
  5. Quadrature detector: the phase angle between consecutive samples becomes the audio frequency deviation.
  6. Low-pass filter, then drop samples to reduce the rate and lighten every later stage.

AM envelope demod

# amplitude = distance from origin
amp   = hypot(I, Q)
audio = lowpass(amp)

FM quadrature demod

# phase = angle of the IQ vector
phase = atan2(Q, I)
audio = diff(unwrap(phase))

Tools

From raw capture to full GUI receivers — these cover most workflows.

  • rtl_sdr — Raw IQ capture to a file from RTL-SDR dongles.
  • rtl_fm — Narrowband/wideband FM demod; pipe audio to a player.
  • rtl_power — Spectrum scanner — sweep a band to a CSV log/heatmap.
  • rtl_433 — Decodes 433/868/915 MHz ISM sensors (weather, TPMS, remotes).
  • gqrx — GUI receiver: waterfall, demod, audio (uses GNU Radio).
  • SDR++ — Modern cross-platform GUI receiver (RTL-SDR, Airspy, HackRF).
  • SDR# — Windows GUI receiver for RTL-SDR and Airspy.
  • GNU Radio — Flowgraph framework (3.10.x) — build the DSP yourself.
  • SDRAngel — Multi-mode receiver/transmitter with deep feature set.

Capture raw IQ

rtl_sdr -f 100e6 -s 2.4e6 -g 20 capture.bin

Record 2.4 MHz of spectrum around 100 MHz to a file.

Live FM audio

rtl_fm -f 100.1e6 -M wbfm -s 200k -r 48k - | \
  aplay -r 48k -f S16_LE

Demodulate and play a broadcast FM station on Linux.

FlagModulationUse for
-M fmnarrowband FM2-way radio, ham repeaters, NOAA APT
-M wbfmwideband FMbroadcast FM radio
-M amAM envelopeairband, AM broadcast
-M rawno demodpass IQ straight through

⌁: Install: brew install rtl-sdr rtl-433 gnuradio (macOS) or sudo apt install rtl-sdr rtl-433 gqrx-sdr gnuradio (Debian/Ubuntu). Blacklist the DVB-T kernel driver so the dongle is free for SDR use. GNU Radio 3.10.x is the current stable line.

Common signals

Frequencies and decoders for the signals people actually go hunting for.

SignalFrequencyModulationDecoder
AM broadcast530–1700 kHzAM, ~10 kHz channelsany AM receiver
FM broadcast88–108 MHzWFM, 200 kHz, 19 kHz pilotrtl_fm -M wbfm
NOAA APT137.1 (NOAA-19) / 137.62 (NOAA-15) / 137.9125 (NOAA-18) MHzWFM, 34 kHznoaa-apt
ADS-B1090 MHzpulse-position, Mode-Sdump1090
POCSAG (paging)138 / 153 / 929 MHzFSKmultimon-ng
ACARS131.550 MHz (VHF)AFSKacarsdec
LoRa433 / 868 / 915 MHzchirp spread spectrumLoRa hardware / SDR toolkits

noaa-apt dump1090 multimon-ng acarsdec rtl_fm

📡: Antenna matters. ADS-B needs a tuned 1090 MHz antenna (ideally with ground plane); NOAA APT wants a VHF turnstile or QFH. Most “no signal” problems are antenna problems, not software problems.

More signals to try
SignalFrequencyNote
AIS161.975 / 162.025 MHzship tracking, GMSK
RTTY / FT8HF amateur bandsdata modes via fldigi / wsjt-x
SSTV145.800 MHz (ISS)slow-scan TV images
Meteor-M2-3 / M2-4137.1 / 137.9 MHzLRPT weather imagery

GNU Radio flowgraphs

Connect blocks in a graph: samples flow from a source through filters and demodulators to a sink.

  1. RTL-SDR Source or File Source pulls IQ samples into the graph.
  2. Low Pass Filter or Band Pass Filter selects the channel and often decimates.
  3. WBFM Receive, AM Demod, or Quadrature Demod turns IQ into audio.
  4. Audio Sink, File Sink, or QT GUI Sink plays, saves, or visualizes.

GRC (the GUI)

Drag blocks onto a canvas and wire them. The .grc file compiles to Python you can read and edit.

# run a generated flowgraph
python3 fm_receiver.py

Python flowgraph

Everything GRC does is plain Python — the same blocks, hand-wired.

from gnuradio import gr, blocks, filter, analog, audio

tb = gr.top_block()
src = blocks.file_source(gr.sizeof_gr_complex, "capture.bin")
lp  = filter.fir_filter_ccf(1, filter.firdes.low_pass(1, 2.4e6, 100e3, 10e3))
dem = analog.wfm_rcv(quad_rate=2.4e6, audio_decimation=10)
sink = audio.sink(48000)

tb.connect(src, lp, dem, sink)
tb.run()
BlockRoleKey params
RTL-SDR SourcePull IQ from the donglesample_rate, frequency, gain
Low Pass FilterBand-limit + decimatecutoff_freq, transition_width, decim
WBFM ReceiveWideband FM demodquadrature_rate, audio_decimation
AM DemodEnvelope demodchannel_rate
Rational ResamplerChange sample rateinterpolation, decimation
Audio SinkPlay to speakerssample_rate
QT GUI Sink / File SinkVisualize / savefilename (File Sink)

Pitfalls

Things that bite everyone on their first capture.

DC spike at 0 Hz

Every SDR shows a strong spike at the tuned center frequency from DC offset and tuner leakage. It’s not a real signal — ignore it or enable DC blocking.

# offset the center to move the spike
rtl_sdr -f 100.05e6 -s 2.4e6 out.bin

Sample drops

“Lost samples” or USB overruns mean the host can’t keep up. Lower the sample rate, enlarge buffers, or use a shorter/better USB cable.

rtl_sdr -f 100e6 -s 1.0e6 out.bin   # slower
rtl_sdr -f 100e6 -s 2.4e6 -b 0 out.bin  # bigger buffer

Gain settings

Auto gain hides weak signals and pumps up noise; manual gain is predictable. Too low and you hear only hiss; too high and strong stations intermodulate.

rtl_sdr -f 100e6 -s 2.4e6 -g 20 out.bin

Antenna, not software

A random indoor whip is the weakest link. Match the antenna to the band: a dipole cut for the frequency, a ground plane for ADS-B, a turnstile/QFH for APT.

# dipole length ≈ 143 / f(MHz) metres, per leg
# 100 MHz → ~1.43 m total

Front-end overload

A nearby broadcast FM tower can saturate the LNA and appear as ghosts across the whole band. Add an FM band-stop filter, or reduce gain and move the center frequency.

# avoid tuning straight into the strong signal
rtl_sdr -f 1090e6 -s 2.4e6 -g 10 adsb.bin

Listening vs transmitting

RTL-SDR dongles are receive-only, which is generally fine. HackRF and similar can transmit, and transmitting without a license is illegal in most jurisdictions — don’t.

# receive-only: no license required in most places
# transmitting:  requires a license — always

Clock drift (PPM)

Cheap crystal oscillators drift a few ppm, so every signal sits slightly off-frequency. The RTL-SDR V4 ships a 1 ppm TCXO, but older dongles need a correction like -p 42.

# calibrate against a known signal
rtl_fm -f 100e6 -M fm -s 200k -p 42 -

⚠: Stay on the receive side. Receiving law varies by country, and decoding some services (paging, aircraft) is restricted in places. Transmitting on licensed bands without authorization is illegal almost everywhere — a HackRF is not a license.