uSDR-pico/dsp_tim.c

243 wiersze
6.9 KiB
C
Czysty Zwykły widok Historia

/*
* dsp_tim.c
* ==>TO BE INCLUDED IN dsp.c
*
* Created: May 2022
* Author: Arjan te Marvelde
*
* Signal processing of RX and TX branch, to be run on the second processor core.
* Each branch has a dedicated routine that must run on set times.
* The period is determined by reads from the inter-core fifo, by the dsp_loop() routine.
* This fifo is written from core0 from a 16us timer callback routine (i.e. 62.5kHz)
*
* The RX branch:
* - Sample I and Q QSD channels, and shift into I and Q delay line (62.5 kHz per channel)
* - Low pass filter: Fc=4kHz
* - Quarter rate (15.625 kHz) to improve low freq behavior of Hilbert transform
* - Calculate 15 tap Hilbert transform on Q
* - Demodulate, taking proper delays into account
* - Push to Audio output DAC
*
* Always perform audio sampling (62.5kHz) and level detections, in case of VOX active
*
* The TX branch (if VOX or PTT):
* - Low pass filter: Fc=3kHz
* - Eight rate (7.8125 kHz) to improve low F behavior of Hilbert transform
* - Generate Q samples by doing a Hilbert transform
* - Push I and Q to QSE output DACs
*
*/
#include "uSDR.h"
volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample values
/*
* Low pass FIR filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/)
* Settings: sample rates 62500, 31250 or 15625 Hz, stopband -40dB, passband ripple 5dB
* Note: 8 bit precision, so divide sum by 256 (this could be improved when 32bit accumulator)
*/
int16_t lpf3_62[15] = { 3, 3, 5, 7, 9, 10, 11, 11, 11, 10, 9, 7, 5, 3, 3}; // Pass: 0-3000, Stop: 6000-31250
int16_t lpf3_31[15] = { -2, -3, -3, 1, 10, 21, 31, 35, 31, 21, 10, 1, -3, -3, -2}; // Pass: 0-3000, Stop: 6000-15625
int16_t lpf3_15[15] = { 3, 4, -3,-14,-15, 6, 38, 53, 38, 6,-15,-14, -3, 4, 3}; // Pass: 0-3000, Stop: 4500-7812
int16_t lpf7_62[15] = { -2, -1, 1, 7, 16, 26, 33, 36, 33, 26, 16, 7, 1, -1, -2}; // Pass: 0-7000, Stop: 10000-31250
int16_t lpf7_31[15] = { -1, 4, 9, 2,-12, -2, 40, 66, 40, -2,-12, 2, 9, 4, -1}; // Pass: 0-7000, Stop: 10000-15625
int16_t lpf15_62[15] = { -1, 3, 12, 6,-12, -4, 40, 69, 40, -4,-12, 6, 12, 3, -1}; // Pass: 0-15000, Stop: 20000-31250
/** CORE1: RX Branch **/
/*
* Execute RX branch signal processing
* max time to spend is <64us (TIM_US)
* The pre-processed I/Q samples are passed in i_sample and q_sample
* The calculated A sample is passed in a_sample
*/
volatile int32_t i_s_raw[15], q_s_raw[15]; // Raw I/Q samples minus DC bias
volatile int32_t i_s[15], q_s[15]; // Filtered I/Q samples
bool __not_in_flash_func(rx)(void)
{
int32_t q_accu, i_accu;
int32_t qh;
uint16_t i;
/*** SAMPLING ***/
/*
* Shift-in I and Q raw samples
*/
for (i=0; i<14; i++) // Store preprocessed samples in shift registers
{
q_s_raw[i] = q_s_raw[i+1];
i_s_raw[i] = i_s_raw[i+1];
}
q_s_raw[14] = q_sample;
i_s_raw[14] = i_sample;
/*
* Low pass FIR filter
*/
q_accu = 0; // Initialize accumulators
i_accu = 0;
for (i=0; i<15; i++) // Low pass FIR filter
{
q_accu += (int32_t)q_s_raw[i]*lpf3_15[i]; // Fc=3kHz, at 15625 Hz sampling
i_accu += (int32_t)i_s_raw[i]*lpf3_15[i]; // Fc=3kHz, at 15625 Hz sampling
}
q_accu = q_accu/256;
i_accu = i_accu/256;
for (i=0; i<14; i++) // Store filtered samples in shift registers
{
q_s[i] = q_s[i+1];
i_s[i] = i_s[i+1];
}
q_s[14] = q_accu;
i_s[14] = i_accu;
/*** DEMODULATION ***/
switch (dsp_mode)
{
case MODE_USB:
/*
* USB demodulate: I[7] - Qh,
* Qh is Classic Hilbert transform 15 taps, 12 bits
* (see Iowa Hills calculator)
*/
q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L +
(q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L;
qh = q_accu / 4096L;
a_sample = i_s[7] - qh;
break;
case MODE_LSB:
/*
* LSB demodulate: I[7] + Qh,
* Qh is Classic Hilbert transform 15 taps, 12 bits
* (see Iowa Hills calculator)
*/
q_accu = (q_s[0]-q_s[14])*315L + (q_s[2]-q_s[12])*440L +
(q_s[4]-q_s[10])*734L + (q_s[6]-q_s[ 8])*2202L;
qh = q_accu / 4096L;
a_sample = i_s[7] + qh;
break;
case MODE_AM:
/*
* AM demodulate: sqrt(sqr(i)+sqr(q))
* Approximated with mag(i,q)
*/
a_sample = mag(i_s[7], q_s[7]);
break;
default:
break;
}
/*** AUDIO GENERATION ***/
/*
* Scale and clip output,
* Send to audio DAC output
*/
2022-07-14 19:15:09 +00:00
a_sample = (a_sample/64) + DAC_BIAS; // -18dB and add bias level
if (a_sample > DAC_RANGE) // Clip to DAC range
a_sample = DAC_RANGE;
else if (a_sample<0)
a_sample = 0;
return true;
}
/** CORE1: TX branch **/
/*
* Execute TX branch signal processing,
* max time to spend is <64us (TIM_US)
* The pre-processed audio sample is passed in a_sample
* The calculated I and Q samples are passed in i_sample and q_sample
*/
volatile int16_t a_s_raw[15]; // Raw samples, minus DC bias
volatile int16_t a_s[15]; // Filtered and decimated samplesvolatile int16_t
bool __not_in_flash_func(tx)(void)
{
int32_t a_accu, q_accu;
int16_t qh;
int i;
uint16_t i_dac, q_dac;
/*** RAW Audio ***/
for (i=0; i<14; i++) // and store in shift register
a_s_raw[i] = a_s_raw[i+1];
a_s_raw[14] = a_sample;
/*** Low pass filter ***/
a_accu = 0; // Initialize accumulator
for (i=0; i<15; i++) // Low pass FIR filter, using raw samples
a_accu += (int32_t)a_s_raw[i]*lpf3_15[i]; // Fc=3kHz, at 15.625 kHz sampling
for (i=0; i<14; i++) // Shift decimated samples
a_s[i] = a_s[i+1];
a_s[14] = a_accu / 256; // Store rescaled accumulator
/*** MODULATION ***/
switch (dsp_mode)
{
case 0: // USB
/*
* qh is Classic Hilbert transform 15 taps, 12 bits
* (see Iowa Hills calculator)
*/
q_accu = (a_s[0]-a_s[14])*315L + (a_s[2]-a_s[12])*440L +
(a_s[4]-a_s[10])*734L + (a_s[6]-a_s[ 8])*2202L;
qh = -(q_accu / 4096L); // USB: sign is negative
break;
case 1: // LSB
/*
* qh is Classic Hilbert transform 15 taps, 12 bits
* (see Iowa Hills calculator)
*/
q_accu = (a_s[0]-a_s[14])*315L + (a_s[2]-a_s[12])*440L +
(a_s[4]-a_s[10])*734L + (a_s[6]-a_s[ 8])*2202L;
qh = q_accu / 4096L; // LSB: sign is positive
break;
case 2: // AM
/*
* I and Q values are identical
*/
qh = a_s[7];
break;
default:
break;
}
/*
* Write I and Q to QSE DACs, phase is 7 samples back.
* Need to multiply AC with DAC_RANGE/ADC_RANGE (appr 1/8)
* Any case: clip to range
*/
a_accu = DAC_BIAS - (qh/8);
if (a_accu<0)
q_sample = 0;
else if (a_accu>(DAC_RANGE-1))
q_sample = DAC_RANGE-1;
else
q_sample = a_accu;
a_accu = DAC_BIAS + (a_s[7]/8);
if (a_accu<0)
i_sample = 0;
else if (a_accu>(DAC_RANGE-1))
i_sample = DAC_RANGE-1;
else
i_sample = a_accu;
return true;
}