dsp #
Digital signal processing building blocks for demodulating satellite signals.
Overview #
dsp is the front end of a software-defined radio receiver: the part
between the raw IQ samples of an RTL-SDR dongle and the demodulator.
Whatever the signal, that part filters, decimates, fixes the gain and, when
debugging, looks at the spectrum. The package has complex arithmetic, FIR
and root-raised-cosine filters, a decimator, AGC, an IQ file decoder, an FFT
and a short-time Fourier transform. Blocks that process one sample at a time
carry their own state, so memory stays bounded however long the capture.
demod and rtlsdr use it.
Some background, from Proakis and Salehi (Digital Communications, section 9.2):
- The transmitter shapes each symbol with a root-raised-cosine (RRC) filter. The receiver runs the same filter (hence "matched filter"), and the two in series give a raised-cosine pulse. That pulse maximises SNR at the sampling instant and is zero at the neighbouring symbols' instants, so there is no inter-symbol interference.
- The roll-off is the excess bandwidth over the symbol rate; 0.35 means 35%.
- Decimating by N keeps one sample in N. A low-pass filter runs first, otherwise the high frequencies fold back (alias) into the band.
- AGC scales samples towards a target amplitude, so later stages see a constant level whatever the received power.
Installation #
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam install dsp
Usage #
The matched filter runs on the in-phase (I) and quadrature (Q) components
separately. A Fir.t owns its delay line, so it serves one stream only, and
a complex signal needs two filters built from the same taps:
(* Design a root-raised-cosine matched filter, one per component *)
let rrc () =
Dsp.Fir.root_raised_cosine ~samples_per_symbol:4 ~rolloff:0.35 ~num_taps:41
let rrc_i = rrc ()
let rrc_q = rrc ()
(* Read raw IQ samples and filter them *)
let samples = Dsp.Iq.of_bytes (Bytes.make 16 '\128')
let filtered =
Array.map
(fun s ->
Dsp.Complex.v
(Dsp.Fir.apply rrc_i s.Dsp.Complex.re)
(Dsp.Fir.apply rrc_q s.im))
samples
(* Automatic gain control *)
let agc = Dsp.Agc.v ~target:1.0 ~bandwidth:0.05
let agc', normalized = Dsp.Agc.apply agc (Dsp.Complex.v 0.5 0.3)
(* Decimate by 4 with anti-alias filter *)
let lpf = Dsp.Fir.lowpass ~cutoff:0.25 ~num_taps:31
let dec = Dsp.Decimator.v ~filter:lpf 4
let out = Dsp.Decimator.apply_array dec [| 0.; 0.25; 0.5; 0.25; 0. |]
API #
Complex: complex numbers with arithmetic, norm, phase and polar conversion.Fir: FIR filters (v,apply,lowpass,root_raised_cosine,coefficients).Decimator: sample-rate reduction (v,apply,apply_array).Agc: automatic gain control (v,apply,gain).Iq: IQ sample decoding from interleaved unsigned bytes (of_bytes,of_file).Fft: radix-2 FFT and its inverse, spectrum shift and bin frequencies.Stft: a streaming short-time Fourier transform over overlapping windows.
Licence #
ISC License. See LICENSE.md for details.
- OCaml 93%
- Python 5%
- Shell 2%