Files
Uladzimir KarpenkaandClaude Opus 4.8 65cb3c386e 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>
2026-07-10 07:43:07 +03:00

1095 lines
41 KiB
C

/* RXA.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2014, 2015, 2016, 2023, 2025 Warren Pratt, NR0V
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.h"
struct _rxa rxa[MAX_CHANNELS];
void create_rxa (int channel)
{
rxa[channel].mode = RXA_LSB;
rxa[channel].inbuff = (double *) malloc0 (1 * ch[channel].dsp_insize * sizeof (complex));
rxa[channel].outbuff = (double *) malloc0 (1 * ch[channel].dsp_outsize * sizeof (complex));
rxa[channel].midbuff = (double *) malloc0 (2 * ch[channel].dsp_size * sizeof (complex));
// shift to select a slice of spectrum
rxa[channel].shift.p = create_shift (
1, // run
ch[channel].dsp_insize, // input buffer size
rxa[channel].inbuff, // pointer to input buffer
rxa[channel].inbuff, // pointer to output buffer
ch[channel].in_rate, // samplerate
0.0); // amount to shift (Hz)
// resample to dsp rate for main processing
rxa[channel].rsmpin.p = create_resample (
0, // run - will be turned ON below if needed
ch[channel].dsp_insize, // input buffer size
rxa[channel].inbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].in_rate, // input samplerate
ch[channel].dsp_rate, // output samplerate
0.0, // select cutoff automatically
0, // select ncoef automatically
1.0); // gain
// signal generator
rxa[channel].gen0.p = create_gen (
0, // run
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
ch[channel].dsp_rate, // sample rate
2); // mode
// adc (input) meter
rxa[channel].adcmeter.p = create_meter (
1, // run
0, // optional pointer to another 'run'
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to buffer
ch[channel].dsp_rate, // samplerate
0.100, // averaging time constant
0.100, // peak decay time constant
rxa[channel].meter, // result vector
rxa[channel].pmtupdate, // locks for meter access
RXA_ADC_AV, // index for average value
RXA_ADC_PK, // index for peak value
-1, // index for gain value
0); // pointer for gain computation
// notch database
rxa[channel].ndb.p = create_notchdb (
0, // master run for all nbp's
1024); // max number of notches
// notched bandpass
rxa[channel].nbp0.p = create_nbp (
1, // run, always runs
0, // run the notches
0, // position
ch[channel].dsp_size, // buffer size
max(2048, ch[channel].dsp_size), // number of coefficients
0, // minimum phase flag
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
-4150.0, // lower filter frequency
-150.0, // upper filter frequency
ch[channel].dsp_rate, // sample rate
0, // wintype
1.0, // gain
1, // auto-increase notch width
1025, // max number of passbands
&rxa[channel].ndb.p); // addr of database pointer
// bandpass for snba
rxa[channel].bpsnba.p = create_bpsnba (
0, // bpsnba run flag
0, // run the notches
0, // position
ch[channel].dsp_size, // size
max(2048, ch[channel].dsp_size), // number of filter coefficients
0, // minimum phase flag
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
ch[channel].dsp_rate, // samplerate
+ 250.0, // abs value of cutoff nearest zero
+ 5700.0, // abs value of cutoff farthest zero
- 5700.0, // current low frequency
- 250.0, // current high frequency
0, // wintype
1.0, // gain
1, // auto-increase notch width
1025, // max number of passbands
&rxa[channel].ndb.p); // addr of database pointer
// send spectrum display
rxa[channel].sender.p = create_sender (
channel == 0, // run
0, // flag
0, // mode
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to input buffer
0, // arg0 <- disp
1, // arg1 <- ss
0, // arg2 <- LO
0); // arg3 <- NOT USED
// S-meter
rxa[channel].smeter.p = create_meter (
1, // run
0, // optional pointer to another 'run'
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to buffer
ch[channel].dsp_rate, // samplerate
0.100, // averaging time constant
0.100, // peak decay time constant
rxa[channel].meter, // result vector
rxa[channel].pmtupdate, // locks for meter access
RXA_S_AV, // index for average value
RXA_S_PK, // index for peak value
-1, // index for gain value
0); // pointer for gain computation
// AM squelch
rxa[channel].amsq.p = create_amsq (
0, // run
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to signal input buffer used by xamsq
rxa[channel].midbuff, // pointer to signal output buffer used by xamsq
rxa[channel].midbuff, // pointer to trigger buffer that xamsqcap will capture
ch[channel].dsp_rate, // sample rate
0.010, // time constant for averaging signal level
0.070, // signal up transition time
0.070, // signal down transition time
0.009, // signal level to initiate tail
0.010, // signal level to initiate unmute
0.000, // minimum tail length
1.500, // maximum tail length
0.0); // muted gain
// AM demod
rxa[channel].amd.p = create_amd (
0, // run - OFF by default
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
0, // mode: 0->AM, 1->SAM
1, // levelfade: 0->OFF, 1->ON
0, // sideband mode: 0->OFF
ch[channel].dsp_rate, // sample rate
-2000.0, // minimum lock frequency
+2000.0, // maximum lock frequency
1.0, // zeta
250.0, // omegaN
0.02, // tauR
1.4); // tauI
// FM demod
rxa[channel].fmd.p = create_fmd (
0, // run
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_rate, // sample rate
5000.0, // deviation
300.0, // f_low
3000.0, // f_high
-8000.0, // fmin
+8000.0, // fmax
1.0, // zeta
20000.0, // omegaN
0.02, // tau - for dc removal
0.5, // audio gain
1, // run tone filter
254.1, // ctcss frequency
max(2048, ch[channel].dsp_size), // # coefs for de-emphasis filter
0, // min phase flag for de-emphasis filter
max(2048, ch[channel].dsp_size), // # coefs for audio cutoff filter
0); // min phase flag for audio cutoff filter
// FM squelch
rxa[channel].fmsq.p = create_fmsq (
0, // run
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input signal buffer
rxa[channel].midbuff, // pointer to output signal buffer
rxa[channel].fmd.p->audio, // pointer to trigger buffer
ch[channel].dsp_rate, // sample rate
5000.0, // cutoff freq for noise filter (Hz)
&rxa[channel].fmd.p->pllpole, // pointer to pole frequency of the fmd pll (Hz)
0.100, // delay time after channel flush
0.001, // tau for noise averaging
0.100, // tau for long noise averaging
0.050, // signal up transition time
0.010, // signal down transition time
0.750, // noise level to initiate tail
0.562, // noise level to initiate unmute
0.000, // minimum tail time
1.200, // maximum tail time
max(2048, ch[channel].dsp_size), // number of coefficients for noise filter
0); // minimum phase flag
// WFM demod
rxa[channel].wfmd.p = create_wfmd (
0, // run
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_rate, // sample rate
75000.0, // deviation
20.0, // f_low
15000.0, // f_high
0.02, // tau - for dc removal
1, // run de-emphasis
75.0e-6, // de-emphasis time constant
0.5, // audio gain
max(2048, ch[channel].dsp_size), // # coefs for audio cutoff filter
0); // min phase flag for audio cutoff filter
// snba
rxa[channel].snba.p = create_snba (
0, // run
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
ch[channel].dsp_rate, // input / output sample rate
12000, // internal processing sample rate
ch[channel].dsp_size, // buffer size
4, // overlap factor to use
256, // frame size to use; sized for 12K rate
64, // asize
2, // npasses
8.0, // k1
20.0, // k2
10, // b
2, // pre
2, // post
0.5, // pmultmin
200.0, // output resampler low cutoff
5400.0); // output resampler high cutoff
// EQ
{
double default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0};
//double default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0};
double default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
rxa[channel].eqp.p = create_eqp (
0, // run - OFF by default
ch[channel].dsp_size, // buffer size
max(2048, ch[channel].dsp_size), // number of filter coefficients
0, // minimum phase flag
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
10, // number of frequencies
default_F, // frequency vector
default_G, // gain vector
0, // cutoff mode
0, // wintype
ch[channel].dsp_rate); // sample rate
}
// ANF
rxa[channel].anf.p = create_anf (
0, // run - OFF by default
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ANF_DLINE_SIZE, // dline_size
64, // taps
16, // delay
0.0001, // two_mu
0.1, // gamma
1.0, // lidx
0.0, // lidx_min
200.0, // lidx_max
6.25e-12, // ngamma
6.25e-10, // den_mult
1.0, // lincr
3.0); // ldecr
// ANR
rxa[channel].anr.p = create_anr (
0, // run - OFF by default
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ANR_DLINE_SIZE, // dline_size
64, // taps
16, // delay
0.0001, // two_mu
0.1, // gamma
120.0, // lidx
120.0, // lidx_min
200.0, // lidx_max
0.001, // ngamma
6.25e-10, // den_mult
1.0, // lincr
3.0); // ldecr
// EMNR
rxa[channel].emnr.p = create_emnr (
0, // run
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
4096, // FFT size
4, // overlap
ch[channel].dsp_rate, // samplerate
0, // window type
1.0, // gain
2, // gain method
0, // npe_method
1); // ae_run
// RNNoise based noise reduction // NR3 + NR4 support (nr3)
rxa[channel].rnnr.p = create_rnnr (
0, // run
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
ch[channel].dsp_rate); // samplerate
// libspecbleach based noise reduction // NR3 + NR4 support (nr4)
rxa[channel].sbnr.p = create_sbnr(
0, // run
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // input buffer
rxa[channel].midbuff, // output buffer
ch[channel].dsp_rate); // samplerate
// AGC
rxa[channel].agc.p = create_wcpagc (
1, // run
3, // mode
1, // peakmode = envelope
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_size, // buffer size
ch[channel].dsp_rate, // sample rate
0.001, // tau_attack
0.250, // tau_decay
4, // n_tau
10000.0, // max_gain
1.5, // var_gain
1000.0, // fixed_gain
1.0, // max_input
1.0, // out_target
0.250, // tau_fast_backaverage
0.005, // tau_fast_decay
5.0, // pop_ratio
1, // hang_enable
0.500, // tau_hang_backmult
0.250, // hangtime
0.250, // hang_thresh
0.100); // tau_hang_decay
// agc gain meter
rxa[channel].agcmeter.p = create_meter (
1, // run
0, // optional pointer to another 'run'
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to buffer
ch[channel].dsp_rate, // samplerate
0.100, // averaging time constant
0.100, // peak decay time constant
rxa[channel].meter, // result vector
rxa[channel].pmtupdate, // locks for meter access
RXA_AGC_AV, // index for average value
RXA_AGC_PK, // index for peak value
RXA_AGC_GAIN, // index for gain value
&rxa[channel].agc.p->gain); // pointer for gain computation
// bandpass filter
rxa[channel].bp1.p = create_bandpass (
1, // run - used only with ( AM || ANF || ANR || EMNR)
0, // position
ch[channel].dsp_size, // buffer size
max(2048, ch[channel].dsp_size), // number of coefficients
0, // flag for minimum phase
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
-4150.0, // lower filter frequency
-150.0, // upper filter frequency
ch[channel].dsp_rate, // sample rate
1, // wintype
1.0); // gain
// pull phase & scope display data
rxa[channel].sip1.p = create_siphon (
1, // run - needed only for phase display
0, // position
0, // mode
0, // disp
ch[channel].dsp_size, // size of input buffer
rxa[channel].midbuff, // input buffer
4096, // number of samples to store
4096, // fft size for spectrum
0); // specmode
// carrier block
rxa[channel].cbl.p = create_cbl (
0, // run - needed only if set to ON
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
0, // mode
ch[channel].dsp_rate, // sample rate
0.02); // tau
// double-pole CW filter
rxa[channel].doublepole.p = create_doublepole (
0, // run
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
600.0, // center frequency
100.0, // bandwidth
ch[channel].dsp_rate, // sample rate
2.0, // gain
2 ); // mode
// matched CW filter
rxa[channel].matched.p = create_matched (
0, // run
0, // position
ch[channel].dsp_size, // buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
600.0, // center frequency
100.0, // bandwidth
ch[channel].dsp_rate, // sample rate
2.0, // gain
2 ); // mode
// gaussian peaking filter
rxa[channel].gaussian.p = create_gaussian (
0, // run
0, // position
ch[channel].dsp_size, // buffer size
0, // number of coefficients
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
600.0, // center frequency
100.0, // bandwidth
ch[channel].dsp_rate, // sample rate
2.0, // gain
3.0, // nsigma
2 ); // mode
// bi-quad peaking filter
rxa[channel].speak.p = create_speak (
0, // run
ch[channel].dsp_size, // buffer size,
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_rate, // sample rate
600.0, // center frequency
100.0, // bandwidth
2.0, // gain
4, // number of stages
1); // design
// multiple peak filter
{
int def_enable[2] = {1, 1};
double def_freq[2] = {2125.0, 2295.0};
double def_bw[2] = {75.0, 75.0};
double def_gain[2] = {1.0, 1.0};
rxa[channel].mpeak.p = create_mpeak (
0, // run
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_rate, // sample rate
2, // number of peaking filters
def_enable, // enable vector
def_freq, // frequency vector
def_bw, // bandwidth vector
def_gain, // gain vector
4 ); // number of stages
}
// apf_shadow
rxa[channel].apfshadow.p = create_apfshadow (
0, // selection
0, // run
600.0, // center frequency
100.0, // bandwidth
2.0 ); // gain
// syllabic squelch
rxa[channel].ssql.p = create_ssql(
0, // run
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
ch[channel].dsp_rate, // sample rate
0.070, // signal up transition time
0.070, // signal down transition time
0.0, // muted gain
0.1, // mute time-constant
0.1, // unmute time-constant
0.08, // window threshold
0.8197, // trigger threshold
2400, // ring size for f_to_v converter
2000.0); // max freq for f_to_v converter
// patchpanel
rxa[channel].panel.p = create_panel (
channel, // channel number
1, // run
ch[channel].dsp_size, // size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].midbuff, // pointer to output buffer
4.0, // gain1
1.0, // gain2I
1.0, // gain2Q
3, // 3 for I and Q
0); // no copy
// resample
rxa[channel].rsmpout.p = create_resample (
0, // run - will be turned ON below if needed
ch[channel].dsp_size, // input buffer size
rxa[channel].midbuff, // pointer to input buffer
rxa[channel].outbuff, // pointer to output buffer
ch[channel].dsp_rate, // input sample rate
ch[channel].out_rate, // output sample rate
0.0, // select cutoff automatically
0, // select ncoef automatically
1.0); // gain
// turn OFF / ON resamplers as needed
RXAResCheck (channel);
}
void destroy_rxa (int channel)
{
destroy_resample (rxa[channel].rsmpout.p);
destroy_panel (rxa[channel].panel.p);
destroy_ssql (rxa[channel].ssql.p);
destroy_apfshadow(rxa[channel].apfshadow.p);
destroy_mpeak (rxa[channel].mpeak.p);
destroy_speak (rxa[channel].speak.p);
destroy_gaussian (rxa[channel].gaussian.p);
destroy_matched (rxa[channel].matched.p);
destroy_doublepole (rxa[channel].doublepole.p);
destroy_cbl (rxa[channel].cbl.p);
destroy_siphon (rxa[channel].sip1.p);
destroy_bandpass (rxa[channel].bp1.p);
destroy_meter (rxa[channel].agcmeter.p);
destroy_wcpagc (rxa[channel].agc.p);
destroy_emnr (rxa[channel].emnr.p);
destroy_rnnr (rxa[channel].rnnr.p); // NR3 + NR4 support (nr3)
destroy_sbnr (rxa[channel].sbnr.p); // NR3 + NR4 support (nr4)
destroy_anr (rxa[channel].anr.p);
destroy_anf (rxa[channel].anf.p);
destroy_eqp (rxa[channel].eqp.p);
destroy_snba (rxa[channel].snba.p);
destroy_wfmd (rxa[channel].wfmd.p);
destroy_fmsq (rxa[channel].fmsq.p);
destroy_fmd (rxa[channel].fmd.p);
destroy_amd (rxa[channel].amd.p);
destroy_amsq (rxa[channel].amsq.p);
destroy_meter (rxa[channel].smeter.p);
destroy_sender (rxa[channel].sender.p);
destroy_bpsnba (rxa[channel].bpsnba.p);
destroy_nbp (rxa[channel].nbp0.p);
destroy_notchdb (rxa[channel].ndb.p);
destroy_meter (rxa[channel].adcmeter.p);
destroy_gen (rxa[channel].gen0.p);
destroy_resample (rxa[channel].rsmpin.p);
destroy_shift (rxa[channel].shift.p);
_aligned_free (rxa[channel].midbuff);
_aligned_free (rxa[channel].outbuff);
_aligned_free (rxa[channel].inbuff);
}
void flush_rxa (int channel)
{
memset (rxa[channel].inbuff, 0, 1 * ch[channel].dsp_insize * sizeof (complex));
memset (rxa[channel].outbuff, 0, 1 * ch[channel].dsp_outsize * sizeof (complex));
memset (rxa[channel].midbuff, 0, 2 * ch[channel].dsp_size * sizeof (complex));
flush_shift (rxa[channel].shift.p);
flush_resample (rxa[channel].rsmpin.p);
flush_gen (rxa[channel].gen0.p);
flush_meter (rxa[channel].adcmeter.p);
flush_nbp (rxa[channel].nbp0.p);
flush_bpsnba (rxa[channel].bpsnba.p);
flush_sender (rxa[channel].sender.p);
flush_meter (rxa[channel].smeter.p);
flush_amsq (rxa[channel].amsq.p);
flush_amd (rxa[channel].amd.p);
flush_fmd (rxa[channel].fmd.p);
flush_fmsq (rxa[channel].fmsq.p);
flush_wfmd (rxa[channel].wfmd.p);
flush_snba (rxa[channel].snba.p);
flush_eqp (rxa[channel].eqp.p);
flush_anf (rxa[channel].anf.p);
flush_anr (rxa[channel].anr.p);
flush_emnr (rxa[channel].emnr.p);
flush_wcpagc (rxa[channel].agc.p);
flush_meter (rxa[channel].agcmeter.p);
flush_bandpass (rxa[channel].bp1.p);
flush_siphon (rxa[channel].sip1.p);
flush_cbl (rxa[channel].cbl.p);
flush_doublepole (rxa[channel].doublepole.p);
flush_matched (rxa[channel].matched.p);
flush_gaussian (rxa[channel].gaussian.p);
flush_speak (rxa[channel].speak.p);
flush_mpeak (rxa[channel].mpeak.p);
flush_ssql (rxa[channel].ssql.p);
flush_panel (rxa[channel].panel.p);
flush_resample (rxa[channel].rsmpout.p);
}
void xrxa (int channel)
{
xshift (rxa[channel].shift.p);
xresample (rxa[channel].rsmpin.p);
xgen (rxa[channel].gen0.p);
xmeter (rxa[channel].adcmeter.p);
xbpsnbain (rxa[channel].bpsnba.p, 0);
xnbp (rxa[channel].nbp0.p, 0);
xmeter (rxa[channel].smeter.p);
xsender (rxa[channel].sender.p);
xamsqcap (rxa[channel].amsq.p);
xbpsnbaout (rxa[channel].bpsnba.p, 0);
xamd (rxa[channel].amd.p);
xfmd (rxa[channel].fmd.p);
xfmsq (rxa[channel].fmsq.p);
xwfmd (rxa[channel].wfmd.p);
xbpsnbain (rxa[channel].bpsnba.p, 1);
xbpsnbaout (rxa[channel].bpsnba.p, 1);
xsnba (rxa[channel].snba.p);
xeqp (rxa[channel].eqp.p);
xanf (rxa[channel].anf.p, 0);
xanr (rxa[channel].anr.p, 0);
xemnr (rxa[channel].emnr.p, 0);
xrnnr (rxa[channel].rnnr.p, 0); // NR3 + NR4 support (nr3)
xsbnr (rxa[channel].sbnr.p, 0); // NR3 + NR4 support (nr4)
xbandpass (rxa[channel].bp1.p, 0);
xwcpagc (rxa[channel].agc.p);
xanf (rxa[channel].anf.p, 1);
xanr (rxa[channel].anr.p, 1);
xemnr (rxa[channel].emnr.p, 1);
xrnnr (rxa[channel].rnnr.p, 1); // NR3 + NR4 support (nr3)
xsbnr (rxa[channel].sbnr.p, 1); // NR3 + NR4 support (nr4)
xbandpass (rxa[channel].bp1.p, 1);
xmeter (rxa[channel].agcmeter.p);
xsiphon (rxa[channel].sip1.p, 0);
xcbl (rxa[channel].cbl.p);
xdoublepole (rxa[channel].doublepole.p, 0);
xmatched (rxa[channel].matched.p, 0);
xgaussian (rxa[channel].gaussian.p, 0);
xspeak (rxa[channel].speak.p);
xmpeak (rxa[channel].mpeak.p);
xssql (rxa[channel].ssql.p);
xpanel (rxa[channel].panel.p);
xamsq (rxa[channel].amsq.p);
xresample (rxa[channel].rsmpout.p);
}
void setInputSamplerate_rxa (int channel)
{
// buffers
_aligned_free (rxa[channel].inbuff);
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
// shift
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
setSamplerate_shift (rxa[channel].shift.p, ch[channel].in_rate);
// input resampler
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
setInRate_resample (rxa[channel].rsmpin.p, ch[channel].in_rate);
RXAResCheck (channel);
}
void setOutputSamplerate_rxa (int channel)
{
// buffers
_aligned_free (rxa[channel].outbuff);
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
// output resampler
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
setOutRate_resample (rxa[channel].rsmpout.p, ch[channel].out_rate);
RXAResCheck (channel);
}
void setDSPSamplerate_rxa (int channel)
{
// buffers
_aligned_free (rxa[channel].inbuff);
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
_aligned_free (rxa[channel].outbuff);
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
// shift
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
// input resampler
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
setOutRate_resample (rxa[channel].rsmpin.p, ch[channel].dsp_rate);
// dsp_rate blocks
setSamplerate_gen (rxa[channel].gen0.p, ch[channel].dsp_rate);
setSamplerate_meter (rxa[channel].adcmeter.p, ch[channel].dsp_rate);
setSamplerate_nbp (rxa[channel].nbp0.p, ch[channel].dsp_rate);
setSamplerate_bpsnba (rxa[channel].bpsnba.p, ch[channel].dsp_rate);
setSamplerate_meter (rxa[channel].smeter.p, ch[channel].dsp_rate);
setSamplerate_sender (rxa[channel].sender.p, ch[channel].dsp_rate);
setSamplerate_amsq (rxa[channel].amsq.p, ch[channel].dsp_rate);
setSamplerate_amd (rxa[channel].amd.p, ch[channel].dsp_rate);
setSamplerate_fmd (rxa[channel].fmd.p, ch[channel].dsp_rate);
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
setSamplerate_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_rate);
setSamplerate_wfmd (rxa[channel].wfmd.p, ch[channel].dsp_rate);
setSamplerate_snba (rxa[channel].snba.p, ch[channel].dsp_rate);
setSamplerate_eqp (rxa[channel].eqp.p, ch[channel].dsp_rate);
setSamplerate_anf (rxa[channel].anf.p, ch[channel].dsp_rate);
setSamplerate_anr (rxa[channel].anr.p, ch[channel].dsp_rate);
setSamplerate_emnr (rxa[channel].emnr.p, ch[channel].dsp_rate);
setSamplerate_rnnr(rxa[channel].rnnr.p, ch[channel].dsp_rate); // NR3 + NR4 support (nr3)
setSamplerate_sbnr(rxa[channel].sbnr.p, ch[channel].dsp_rate); // NR3 + NR4 support (nr4)
setSamplerate_bandpass (rxa[channel].bp1.p, ch[channel].dsp_rate);
setSamplerate_wcpagc (rxa[channel].agc.p, ch[channel].dsp_rate);
setSamplerate_meter (rxa[channel].agcmeter.p, ch[channel].dsp_rate);
setSamplerate_siphon (rxa[channel].sip1.p, ch[channel].dsp_rate);
setSamplerate_cbl (rxa[channel].cbl.p, ch[channel].dsp_rate);
setSamplerate_doublepole (rxa[channel].doublepole.p, ch[channel].dsp_rate);
setSamplerate_matched (rxa[channel].matched.p, ch[channel].dsp_rate);
setSamplerate_gaussian (rxa[channel].gaussian.p, ch[channel].dsp_rate);
setSamplerate_speak (rxa[channel].speak.p, ch[channel].dsp_rate);
setSamplerate_mpeak (rxa[channel].mpeak.p, ch[channel].dsp_rate);
setSamplerate_ssql (rxa[channel].ssql.p, ch[channel].dsp_rate);
setSamplerate_panel (rxa[channel].panel.p, ch[channel].dsp_rate);
// output resampler
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
setInRate_resample (rxa[channel].rsmpout.p, ch[channel].dsp_rate);
RXAResCheck (channel);
}
void setDSPBuffsize_rxa (int channel)
{
// buffers
_aligned_free(rxa[channel].inbuff);
rxa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
_aligned_free (rxa[channel].midbuff);
rxa[channel].midbuff = (double *)malloc0(2 * ch[channel].dsp_size * sizeof(complex));
_aligned_free (rxa[channel].outbuff);
rxa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
// shift
setBuffers_shift (rxa[channel].shift.p, rxa[channel].inbuff, rxa[channel].inbuff);
setSize_shift (rxa[channel].shift.p, ch[channel].dsp_insize);
// input resampler
setBuffers_resample (rxa[channel].rsmpin.p, rxa[channel].inbuff, rxa[channel].midbuff);
setSize_resample (rxa[channel].rsmpin.p, ch[channel].dsp_insize);
// dsp_size blocks
setBuffers_gen (rxa[channel].gen0.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_gen (rxa[channel].gen0.p, ch[channel].dsp_size);
setBuffers_meter (rxa[channel].adcmeter.p, rxa[channel].midbuff);
setSize_meter (rxa[channel].adcmeter.p, ch[channel].dsp_size);
setBuffers_nbp (rxa[channel].nbp0.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_nbp (rxa[channel].nbp0.p, ch[channel].dsp_size);
setBuffers_bpsnba (rxa[channel].bpsnba.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_bpsnba (rxa[channel].bpsnba.p, ch[channel].dsp_size);
setBuffers_meter (rxa[channel].smeter.p, rxa[channel].midbuff);
setSize_meter (rxa[channel].smeter.p, ch[channel].dsp_size);
setBuffers_sender (rxa[channel].sender.p, rxa[channel].midbuff);
setSize_sender (rxa[channel].sender.p, ch[channel].dsp_size);
setBuffers_amsq (rxa[channel].amsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_amsq (rxa[channel].amsq.p, ch[channel].dsp_size);
setBuffers_amd (rxa[channel].amd.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_amd (rxa[channel].amd.p, ch[channel].dsp_size);
setBuffers_fmd (rxa[channel].fmd.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_fmd (rxa[channel].fmd.p, ch[channel].dsp_size);
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
setSize_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_size);
setBuffers_wfmd (rxa[channel].wfmd.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_wfmd (rxa[channel].wfmd.p, ch[channel].dsp_size);
setBuffers_snba (rxa[channel].snba.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_snba (rxa[channel].snba.p, ch[channel].dsp_size);
setBuffers_eqp (rxa[channel].eqp.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_eqp (rxa[channel].eqp.p, ch[channel].dsp_size);
setBuffers_anf (rxa[channel].anf.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_anf (rxa[channel].anf.p, ch[channel].dsp_size);
setBuffers_anr (rxa[channel].anr.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_anr (rxa[channel].anr.p, ch[channel].dsp_size);
setBuffers_emnr (rxa[channel].emnr.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_rnnr(rxa[channel].rnnr.p, ch[channel].dsp_size); // NR3 + NR4 support (nr3)
setBuffers_rnnr(rxa[channel].rnnr.p, rxa[channel].midbuff, rxa[channel].midbuff); // NR3 + NR4 support (nr3)
setSize_sbnr(rxa[channel].sbnr.p, ch[channel].dsp_size); // NR3 + NR4 support (nr4)
setBuffers_sbnr (rxa[channel].sbnr.p, rxa[channel].midbuff, rxa[channel].midbuff); // NR3 + NR4 support (nr4)
setSize_emnr (rxa[channel].emnr.p, ch[channel].dsp_size);
setBuffers_bandpass (rxa[channel].bp1.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_bandpass (rxa[channel].bp1.p, ch[channel].dsp_size);
setBuffers_wcpagc (rxa[channel].agc.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_wcpagc (rxa[channel].agc.p, ch[channel].dsp_size);
setBuffers_meter (rxa[channel].agcmeter.p, rxa[channel].midbuff);
setSize_meter (rxa[channel].agcmeter.p, ch[channel].dsp_size);
setBuffers_siphon (rxa[channel].sip1.p, rxa[channel].midbuff);
setSize_siphon (rxa[channel].sip1.p, ch[channel].dsp_size);
setBuffers_cbl (rxa[channel].cbl.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_cbl (rxa[channel].cbl.p, ch[channel].dsp_size);
setBuffers_doublepole (rxa[channel].doublepole.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_doublepole (rxa[channel].doublepole.p, ch[channel].dsp_size);
setBuffers_matched (rxa[channel].matched.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_matched (rxa[channel].matched.p, ch[channel].dsp_size);
setBuffers_gaussian (rxa[channel].gaussian.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_gaussian (rxa[channel].gaussian.p, ch[channel].dsp_size);
setBuffers_speak (rxa[channel].speak.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_speak (rxa[channel].speak.p, ch[channel].dsp_size);
setBuffers_mpeak (rxa[channel].mpeak.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_mpeak (rxa[channel].mpeak.p, ch[channel].dsp_size);
setBuffers_ssql (rxa[channel].ssql.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_ssql (rxa[channel].ssql.p, ch[channel].dsp_size);
setBuffers_panel (rxa[channel].panel.p, rxa[channel].midbuff, rxa[channel].midbuff);
setSize_panel (rxa[channel].panel.p, ch[channel].dsp_size);
// output resampler
setBuffers_resample (rxa[channel].rsmpout.p, rxa[channel].midbuff, rxa[channel].outbuff);
setSize_resample (rxa[channel].rsmpout.p, ch[channel].dsp_size);
}
/********************************************************************************************************
* *
* RXA Mode & Filter Controls *
* *
********************************************************************************************************/
PORT
void SetRXAMode (int channel, int mode)
{
if (rxa[channel].mode != mode)
{
int amd_run = (mode == RXA_AM) || (mode == RXA_SAM);
RXAbpsnbaCheck (channel, mode, rxa[channel].ndb.p->master_run);
RXAbp1Check (channel, amd_run, rxa[channel].snba.p->run, rxa[channel].emnr.p->run,
rxa[channel].anf.p->run, rxa[channel].anr.p->run,
rxa[channel].rnnr.p->run, rxa[channel].sbnr.p->run); // NR3 + NR4 support
EnterCriticalSection (&ch[channel].csDSP);
rxa[channel].mode = mode;
rxa[channel].amd.p->run = 0;
rxa[channel].fmd.p->run = 0;
rxa[channel].wfmd.p->run = 0;
rxa[channel].agc.p->run = 1;
switch (mode)
{
case RXA_AM:
rxa[channel].amd.p->run = 1;
rxa[channel].amd.p->mode = 0;
break;
case RXA_SAM:
rxa[channel].amd.p->run = 1;
rxa[channel].amd.p->mode = 1;
break;
case RXA_DSB:
break;
case RXA_FM:
rxa[channel].fmd.p->run = 1;
rxa[channel].agc.p->run = 0;
break;
case RXA_WFM:
rxa[channel].wfmd.p->run = 1;
rxa[channel].agc.p->run = 0;
break;
default:
break;
}
RXAbp1Set (channel);
RXAbpsnbaSet (channel); // update variables
LeaveCriticalSection (&ch[channel].csDSP);
}
}
void RXAResCheck (int channel)
{
// turn OFF/ON resamplers depending upon whether they're needed
RESAMPLE a = rxa[channel].rsmpin.p;
if (ch[channel].in_rate != ch[channel].dsp_rate) a->run = 1;
else a->run = 0;
a = rxa[channel].rsmpout.p;
if (ch[channel].dsp_rate != ch[channel].out_rate) a->run = 1;
else a->run = 0;
}
void RXAbp1Check (int channel, int amd_run, int snba_run,
int emnr_run, int anf_run, int anr_run,
int rnnr_run, int sbnr_run) // NR3 + NR4 support
{
BANDPASS a = rxa[channel].bp1.p;
double gain;
if (amd_run ||
snba_run ||
emnr_run ||
rnnr_run || // NR3 + NR4 support (nr3)
sbnr_run || // NR3 + NR4 support (nr4)
anf_run ||
anr_run) gain = 2.0;
else gain = 1.0;
if (a->gain != gain)
setGain_bandpass (a, gain, 0);
}
void RXAbp1Set (int channel)
{
BANDPASS a = rxa[channel].bp1.p;
int old = a->run;
if ((rxa[channel].amd.p->run == 1) ||
(rxa[channel].snba.p->run == 1) ||
(rxa[channel].emnr.p->run == 1) ||
(rxa[channel].rnnr.p->run == 1) || // NR3 + NR4 support (nr3)
(rxa[channel].sbnr.p->run == 1) || // NR3 + NR4 support (nr4)
(rxa[channel].anf.p->run == 1) ||
(rxa[channel].anr.p->run == 1)) a->run = 1;
else a->run = 0;
if (!old && a->run) flush_bandpass (a);
setUpdate_fircore (a->p);
}
void RXAbpsnbaCheck (int channel, int mode, int notch_run)
{
// for BPSNBA: set run, position, freqs, run_notches
// call this upon change in RXA_mode, snba_run, notch_master_run
BPSNBA a = rxa[channel].bpsnba.p;
double f_low = 0.0, f_high = 0.0;
int run_notches = 0;
switch (mode)
{
case RXA_LSB:
case RXA_CWL:
case RXA_DIGL:
f_low = -a->abs_high_freq;
f_high = -a->abs_low_freq;
run_notches = notch_run;
break;
case RXA_USB:
case RXA_CWU:
case RXA_DIGU:
f_low = +a->abs_low_freq;
f_high = +a->abs_high_freq;
run_notches = notch_run;
break;
case RXA_AM:
case RXA_SAM:
case RXA_DSB:
f_low = +a->abs_low_freq;
f_high = +a->abs_high_freq;
run_notches = 0;
break;
case RXA_FM:
case RXA_WFM:
f_low = +a->abs_low_freq;
f_high = +a->abs_high_freq;
run_notches = 0;
break;
case RXA_DRM:
case RXA_SPEC:
break;
}
// 'run' and 'position' are examined at run time; no filter changes required.
// Recalculate filter if frequencies OR 'run_notches' changed.
if ((a->f_low != f_low ) ||
(a->f_high != f_high ) ||
(a->run_notches != run_notches))
{
a->f_low = f_low;
a->f_high = f_high;
a->run_notches = run_notches;
// f_low, f_high, run_notches are needed for the filter recalculation
recalc_bpsnba_filter (a, 0);
}
}
void RXAbpsnbaSet (int channel)
{
// for BPSNBA: set run, position, freqs, run_notches
// call this upon change in RXA_mode, snba_run, notch_master_run
BPSNBA a = rxa[channel].bpsnba.p;
switch (rxa[channel].mode)
{
case RXA_LSB:
case RXA_CWL:
case RXA_DIGL:
a->run = rxa[channel].snba.p->run;
a->position = 0;
break;
case RXA_USB:
case RXA_CWU:
case RXA_DIGU:
a->run = rxa[channel].snba.p->run;
a->position = 0;
break;
case RXA_AM:
case RXA_SAM:
case RXA_DSB:
a->run = rxa[channel].snba.p->run;
a->position = 1;
break;
case RXA_FM:
case RXA_WFM:
a->run = rxa[channel].snba.p->run;
a->position = 1;
break;
case RXA_DRM:
case RXA_SPEC:
a->run = 0;
break;
}
setUpdate_fircore (a->bpsnba->p);
}
/********************************************************************************************************
* *
* Collectives *
* *
********************************************************************************************************/
PORT
void RXASetPassband (int channel, double f_low, double f_high)
{
SetRXABandpassFreqs (channel, f_low, f_high);
SetRXASNBAOutputBandwidth (channel, f_low, f_high);
RXANBPSetFreqs (channel, f_low, f_high);
}
PORT
void RXASetNC (int channel, int nc)
{
int oldstate = SetChannelState (channel, 0, 1);
RXANBPSetNC (channel, nc);
RXABPSNBASetNC (channel, nc);
SetRXABandpassNC (channel, nc);
SetRXAEQNC (channel, nc);
SetRXAFMSQNC (channel, nc);
SetRXAFMNCde (channel, nc);
SetRXAFMNCaud (channel, nc);
SetRXAWFMNCaud (channel, nc);
SetChannelState (channel, oldstate, 0);
}
PORT
void RXASetMP (int channel, int mp)
{
RXANBPSetMP (channel, mp);
RXABPSNBASetMP (channel, mp);
SetRXABandpassMP (channel, mp);
SetRXAEQMP (channel, mp);
SetRXAFMSQMP (channel, mp);
SetRXAFMMPde (channel, mp);
SetRXAFMMPaud (channel, mp);
SetRXAWFMMPaud (channel, mp);
}