Linrad Notes
Source Issues

Mixers, filters, baseband and output

The narrowband DSP: from the wideband timf2 to demodulated audio.

Second FFT: AFC and spur removal

second_fft() adds the weak + strong parts of timf2 into one time function, produces the high-resolution fft2 spectrum, and runs spur removal. fft2 feeds AFC and spur removal only; the audio path does not pass through it.

AFC (afcsub.c) tracks weak/unstable signals so the first mixer can center them at 0 Hz. collect_initial_spectrum() acquires a signal (averaged transforms, peak by parabolic interpolation, initial polarization); make_afc_signoi() forms the S/N used to validate the track; make_ag_point() builds the AFC-graph points with noise estimated from filters outside the signal. Longer averaging finds weaker signals at the cost of delay. AFC runs across all receive modes and closes the loop through the first mixer; the tracking loop and its AFC_ENABLE / AFC_LOCK_RANGE / AFC_MAX_DRIFT settings have their own AFC page.

In the source

second_fft()wcw.c:250; make_fft2() in fft2.c; spur_removal() in spur.c / spursub.c (wcw.c:286). AFC: collect_initial_spectrum() afcsub.c:34, make_afc_signoi() afcsub.c:693, make_ag_point() afcsub.c:792, make_afct_window() afcsub.c:992; AG_PARMS ag globdef.h:884.

First mixer: tune + decimate

do_mix1() implements the first mixer as a limited back FFT rather than an NCO + FIR (z_SETTINGS.txt):

Rather than actually mixing … with a digitally controlled oscillator … and then filtering …, limited back FFTs are used. … The filter in use during this process is the window function of the FFT.

A slice of mix1.size bins centered on the selected frequency is multiplied by the frequency-domain window mix1_fqwin[] (the anti-alias/decimation filter, so its sin^p power sets spur suppression), given a residual fine-tuning phase rotation from the AFC error, and inverse-transformed to timf3 at the reduced rate. set_mix1_phases() maintains phase continuity across overlapping transforms.

In the source

do_mix1()mix1.c:55; do_mix1_afc()mix1.c:648; mix1_fqwin window at mix1.c:118; phase rotation at mix1.c:104; set_mix1_phases()mix1.c:781; second-FFT-driven variants fft2_mix1_afc() / fft2_mix1_fixed()mix1.c:863. mix1 / mix2 are MIXER_VARIABLES (uidef.h:37).

Third FFT + second mixer: the baseband filter

do_mix2()fft3_mix2() applies the baseband filter and produces the complex baseband. The third FFT (fft3.c) transforms timf3; mix2 selects mix2.size bins around 0 Hz.

Second mixer and baseband filter

Two methods, by bg.mixer_mode:

Alongside the audio filter, mix2 also extracts a narrow carrier filter bg_carrfilter[] (bg.coh_factor times narrower) used for coherent CW, AM synchronous detection, the S-meter and the phase/AFC displays. In the two-channel branch the polarization combination is applied per bin together with the filter, and the adaptive-coefficient update runs from the same loop (Diversity and adaptive polarization); the main output goes to baseb_raw, the orthogonal polarization to baseb_raw_orthog.

In the source

do_mix2()mix2.c:41; fft3_mix2()mix2.c:83. Frequency-domain filter/decimate: mix2.c:146 (1 ch), mix2.c:600 (2 ch); time-domain FIR mix2.c:217; carrier filter mix2.c:246. Filters are (re)built in baseb_graph.c (make_bg_filter(), make_baseband_graph()).

Baseband processing

make_baseband_graph() turns baseb_raw into baseb_out and drives the baseband spectrum/waterfall. It computes the AGC envelope baseb_agc_level[] (per sample, per channel when bg.agc_flag == 2) with attack/release/hang from bg.agc_attack/bg.agc_release/bg.agc_hang; tracks the AM DC/carrier level for envelope and synchronous AM; demodulates FM (detect_fm(), with WFM pilot / RDS / de-emphasis); extracts the coherent-CW phase from the narrow carrier filter; and applies the baseband notch filters. Parameters are in BG_PARMS bg.

In the source

make_baseband_graph()baseb_graph.c:3249; make_bfo()baseb_graph.c:355; AM DC level baseb_graph.c:935; FM in fm.c and the fm* arrays (sigdef.h:226); coherent CW in coherent.c / cohsub.c; BG_PARMS bg globdef.h:1012.

Output and demodulation

make_audio_signal() produces the D/A stream: squelch gating; fractional resampling to the exact D/A rate via a cubic Lagrange fit to 4 neighbouring baseband samples, with an optional order-5 IIR anti-image filter; AGC gain (pulled down when daout_gain·baseb_agc_level > bg_agc_amplimit, common or per-channel); and the BFO / demodulation selected by rx_mode (MODE_WCW/NCW/HSMS/SSB/FM/AM/QRSS). The baseband can also be streamed over the network.

Output resampling and demodulation

In the source

make_audio_signal()rxout.c:970; squelch rxout.c:995; cubic-Lagrange resampling rxout.c:1068; order-5 IIR rxout.c:1115; AGC gain rxout.c:1087; rx_mode values globdef.h:125. Blocking soundcard path blocking_rxout() rxout.c:80; non-blocking rx_output() rxout.c:266.

Filters in the chain

Filter Where Purpose
fft1_window (sin^p) first FFT analysis + anti-alias for mix1 decimation
liminfo selective limiter sellim.c route strong signals away from the blanker
reference-pulse subtraction blank1.c impulse-noise removal, matched to pulse shape
mix1_fqwin first mixer anti-alias / decimation (frequency-domain window)
spur removal spur.c remove mixing spurs
bg_filterfunc second mixer main passband filter
bg_carrfilter second mixer narrow carrier filter (coherent CW / sync AM / S-meter)
basebraw_fir second mixer (mode 2) time-domain FIR alternative
baseband notches baseb_graph.c manual notch(es) in the passband
polarization combine mix2.c spatial/polarization combination (2 ch)
cubic Lagrange + IIR5 rxout.c fractional resampling / anti-image
FM de-emphasis / pilot / RDS FIRs fm.c WFM broadcast

Baseband subsystems covered separately: Coherent CW and Morse decoding · Spur removal · FM and wideband-FM.