wfm: add a wideband FM modulator and demodulator

Model the pair on fmd.c / fmmod.c, but with the parts that a 75 kHz
deviation forces:

  - the demodulator discriminates with arg(x[n] * conj(x[n-1])) rather
    than a PLL; an omegaN in the tens of kHz cannot track 75 kHz.
  - emphasis is a one-pole RC (tau 75 us) on both ends, not fmd's 1/f
    fc_impulse FIR.  A 1/f FIR from f_low = 20 Hz would sit ~+57 dB at
    20 Hz, where broadcast FM specifies flat below the corner.
  - TXA_WFM leaves the shared preemph block off; wfmmod carries its own.
  - wfmmod clamps bp_fc = deviation + f_high to 0.45 * samplerate, since
    +/-90 kHz exceeds Nyquist at the rates the narrowband modes use.

Scope is mono: no 19 kHz pilot, no 38 kHz stereo subcarrier, no RDS.

Both mode enums are appended to so the ABI stays stable for clients.
The JNI bindings are deliberately left alone.

Verified against a wfmmod -> wfmd loopback: the discriminator is exact,
the dc-removal one-pole tracks |H_lp(f)| * ain * sdelta to ratio 1.000,
and a 700 + 1900 Hz two-tone comes back with THD+N ~ 3e-5 % at +/-37.5
kHz deviation.  Note the demodulator aliases unless samplerate exceeds
2 * deviation * |aud|max, so 192 kHz is the practical floor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-07-10 07:43:07 +03:00
parent f39eea6b01
commit 65cb3c386e
12 changed files with 871 additions and 8 deletions
+24
View File
@@ -517,6 +517,30 @@ extern void SetTXAFMNC (int channel, int nc);
extern void SetTXAFMMP (int channel, int mp);
extern void SetTXAFMAFFreqs (int channel, double low, double high);
//
// Interfaces from wfmd.c
//
extern void SetRXAWFMDeviation (int channel, double deviation);
extern void SetRXAWFMNCaud (int channel, int nc);
extern void SetRXAWFMMPaud (int channel, int mp);
extern void SetRXAWFMAFFilter (int channel, double low, double high);
extern void SetRXAWFMDeemphRun (int channel, int run);
extern void SetRXAWFMDeemphTau (int channel, double tau);
extern void SetRXAWFMLimRun (int channel, int run);
extern void SetRXAWFMLimGain (int channel, double gaindB);
//
// Interfaces from wfmmod.c
//
extern void SetTXAWFMDeviation (int channel, double deviation);
extern void SetTXAWFMNC (int channel, int nc);
extern void SetTXAWFMMP (int channel, int mp);
extern void SetTXAWFMAFFreqs (int channel, double low, double high);
extern void SetTXAWFMPreEmphRun (int channel, int run);
extern void SetTXAWFMPreEmphTau (int channel, double tau);
//
// Interfaces from fmsq.c
//