/*	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

	// 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_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_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);
	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_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_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].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;
		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:
			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:
			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);
	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);
}
