kopia lustrzana https://github.com/ArjanteMarvelde/uSDR-pico
				
				
				
			
		
			
				
	
	
		
			243 wiersze
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			243 wiersze
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
| /*
 | |
|  * 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
 | |
| 	 */
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 |