Added (untested) FFT based TX branch
pull/13/head
ArjanteMarvelde 2022-09-22 20:15:21 +02:00
rodzic a712c1c29b
commit f934f9ccb0
3 zmienionych plików z 255 dodań i 103 usunięć

76
dsp.c
Wyświetl plik

@ -212,8 +212,6 @@ inline int16_t mag(int16_t i, int16_t q)
volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample values
/*** Include the desired DSP engine ***/
#if DSP_FFT == 1
@ -229,17 +227,18 @@ volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample
/** CORE1: ADC IRQ handler **/
/*
* The IRQ handling is redirected to a DMA channel
* This will transfer ADC_INT samples per channel, ADC_INT maximum is 10 (would take 60usec)
* This will transfer ADC_INT samples per channel, ADC_INT maximum is 10 (would take 60usec) but safer to use 8
* These are all registers used for the sample acquisition process
*/
#define LSH 8 // Shift for higher accuracy of level, also LPF
#define ADC_LEVELS (ADC_BIAS/2)<<LSH // Shifted initial ADC level
#define BSH 8 // Shift for higher accuracy of bias, also LPF
#define ADC_BIASS ADC_BIAS<<BSH // Shifted initial ADC bias
#define ADC_INT 8 // Nr of samples for integration (max 8)
volatile int16_t adc_sample[ADC_INT][3]; // ADC samples collection
volatile int32_t adc_bias[3] = {ADC_BIASS, ADC_BIASS, ADC_BIASS}; // ADC dynamic bias level
volatile int32_t adc_result[3]; // ADC filtered result for further processing
volatile uint32_t adc_level[3] = {ADC_LEVELS, ADC_LEVELS, ADC_LEVELS}; // Levels for ADC channels
#define LSH 8 // Left shift for higher accuracy of level, also LPF
#define ADC_LEVELS (ADC_BIAS/2)<<LSH // Left shifted initial ADC level value
#define BSH 8 // Left shift for higher accuracy of bias, also LPF
#define ADC_BIASS ADC_BIAS<<BSH // Left shifted initial ADC bias value
#define ADC_INT 8 // Nr of samples for integration (max 8, depends on e.g. sample rate)
volatile int16_t adc_sample[ADC_INT][3]; // ADC sample collection, filled by DMA
volatile int32_t adc_bias[3] = {ADC_BIASS, ADC_BIASS, ADC_BIASS}; // ADC dynamic bias (DC) level
volatile int32_t adc_result[3]; // ADC bias-filtered result for further processing
volatile uint32_t adc_level[3] = {ADC_LEVELS, ADC_LEVELS, ADC_LEVELS}; // Signa levels for ADC channels
volatile int adccnt = 0; // Sampling overflow indicator
@ -297,12 +296,11 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
{
int32_t temp;
/*
* Here the rate is 15625Hz
*/
/** Here the rate is: S_RATE=1/TIM_US, assume 15625Hz **/
// Get samples and correct for DC bias
// RC: ((1<<BSH)-1)*64usec = 16msec
// Get ADC_INT samples for each channel and correct for DC bias
// LPF RC: ((1<<BSH)-1)*64usec = 16msec
// adc_result is reset every 64usec, adc_bias is not and hence a running average.
adc_result[0] = 0;
adc_result[1] = 0;
adc_result[2] = 0;
@ -316,7 +314,8 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
adc_result[2] += (int32_t)(adc_sample[temp][2]) - (adc_bias[2]>>BSH);
}
// Resstart ADCs and DMA
// Load new acquisition phase
// So restart ADCs and DMA
adccnt--; // ADC overrun indicator decrement
adc_select_input(0); // Start with ADC0
while (!adc_fifo_is_empty()) adc_fifo_get(); // Empty leftovers from fifo, if any
@ -324,18 +323,18 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
dma_hw->ch[CH0].read_addr = (io_rw_32)&adc_hw->fifo; // Read from ADC FIFO
dma_hw->ch[CH0].write_addr = (io_rw_32)&adc_sample[0][0]; // Write to sample buffer
dma_hw->ch[CH0].transfer_count = ADC_INT * 3; // Nr of 16 bit words to transfer
dma_hw->ch[CH0].ctrl_trig = DMA_CTRL0; // Write ctrl word without starting the DMA
dma_hw->ch[CH0].ctrl_trig = DMA_CTRL0; // Write ctrl word while starting the DMA
adc_run(true); // Start ADC again
adc_run(true); // Start ADC too
// Calculate and save level, value is left shifted by LSH = 8
// RC: ((1<<LSH)-1)*64usec = 16msec
// Calculate and save signal level, value is left shifted by LSH = 8
// LPF RC: ((1<<LSH)-1)*64usec = 16msec
adc_level[0] += (ABS(adc_result[0]))-(adc_level[0]>>LSH);
adc_level[1] += (ABS(adc_result[1]))-(adc_level[1]>>LSH);
adc_level[2] += (ABS(adc_result[2]))-(adc_level[2]>>LSH);
// Derive RSSI value from RX vector length
// Crude AGC mechanism **TO BE IMPROVED**
// Crude AGC mechanism **NEEDS TO BE IMPROVED**
if (!tx_enabled)
{
// Approximate amplitude, with alpha max + beta min function
@ -351,44 +350,46 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
#if DSP_FFT == 1
// Copy samples from/to the right buffers
if (tx_enabled)
{
A_buf[dsp_active][dsp_tick] = (int16_t)(tx_agc*adc_result[2]);
A_buf[dsp_active][dsp_tick] = (int16_t)(tx_agc*adc_result[2]); // Copy A sample to A buffer
pwm_set_gpio_level(DAC_I, I_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output I to DAC
pwm_set_gpio_level(DAC_Q, Q_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output Q to DAC
}
else
{
I_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[1]);
Q_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[0]);
I_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[1]); // Copy I sample to I buffer
Q_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[0]); // Copy Q sample to Q buffer
pwm_set_gpio_level(DAC_A, A_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output A to DAC
}
// When sample buffer is full, move pointer to next and signal the DSP loop
// When I, Q or A buffer is full, move pointer to the next and signal the DSP loop
if (++dsp_tick >= BUFSIZE) // Increment tick and check range
{
dsp_tick = 0; // Reset counter
if (++dsp_active > 2) dsp_active = 0; // Rotate offset
if (++dsp_active > 2) dsp_active = 0; // Point to next buffer
dsp_overrun++; // Increment overrun counter
sem_release(&dsp_sem); // Signal DSP loop semaphore
sem_release(&dsp_sem); // Signal background processing
}
#else
// Copy samples from/to the right buffers
if (tx_enabled)
{
a_sample = tx_agc * adc_result[2]; // Store A for DSP use
pwm_set_gpio_level(DAC_I, i_sample); // Output I to DAC
pwm_set_gpio_level(DAC_Q, q_sample); // Output Q to DAC
a_sample = tx_agc * adc_result[2]; // Store A sample for background processing
pwm_set_gpio_level(DAC_I, i_sample); // Output calculated I sample to DAC
pwm_set_gpio_level(DAC_Q, q_sample); // Output calculated Q sample to DAC
}
else
{
pwm_set_gpio_level(DAC_A, a_sample); // Output Q to DAC
q_sample = rx_agc * adc_result[0]; // Store Q for DSP use
i_sample = rx_agc * adc_result[1]; // Store I for DSP use
q_sample = rx_agc * adc_result[0]; // Store Q sample for background processing
i_sample = rx_agc * adc_result[1]; // Store I sample for background processing
pwm_set_gpio_level(DAC_A, a_sample); // Output calculated A sample to DAC
}
dsp_overrun++; // Increment overrun counter
sem_release(&dsp_sem); // Signal DSP loop semaphore
sem_release(&dsp_sem); // Signal background processing
#endif
@ -475,9 +476,10 @@ void __not_in_flash_func(dsp_loop)()
dsp_overrun = 0;
// Background processing loop
while(1)
{
sem_acquire_blocking(&dsp_sem); // Wait until timer callback releases sem
sem_acquire_blocking(&dsp_sem); // Wait until timer-callback releases sem
dsp_overrun--; // Decrement overrun counter
// Use adc_level[2] for VOX

257
dsp_fft.c
Wyświetl plik

@ -26,26 +26,45 @@
* The other two are swapped with the FFT signal processing buffers.
* Since we use complex FFT, the algorithm uses 4x buffers.
*
* I, Q and A buffers used as queues. RX case looks like:
* I, Q and A buffers are used as queues. RX case looks like:
*
* +--+--+--+
* i --> | | | |
* +--+--+--+
* \ \ \ +--+--+
* --------> | | | +--+--+--+
* +--+--+ FFT-DSP-iFFT --> | | | | --> a
* --------> | | | +--+--+--+
* +--+--+--+ +--+--+--+
* i --> | | | | | | | | --> a
* +--+--+--+ +--+--+--+
* \ \ \ +--+--+ / /
* ---------> | | | -------
* +--+--+ FFT-DSP-iFFT
* ---------> | | |
* / / / +--+--+
* +--+--+--+
* q --> | | | |
* +--+--+--+
*
* RX, when triggered by timer callback:
* - The oldest real FFT buffer is moved to the output queue (check this)
* - The oldest two I and Q buffers are copied into the FFT buffers
* - FFT is executed
* - Signal processing is done
* - iFFT is executed
* - The oldest real FFT buffer is moved to the A output queue
*
* +--+--+--+ +--+--+--+
* a --> | | | | | | | | --> i
* +--+--+--+ +--+--+--+
* \ \ \ +--+--+ / /
* --------> | | | -------
* +--+--+ FFT-DSP-iFFT
* | | | -------
* +--+--+ \ \
* +--+--+--+
* | | | | --> q
* +--+--+--+
*
* TX, when triggered by timer callback:
* - The oldest two A buffers are copied to the real FFT buffer, the imaginary FFT buffer is nulled
* - FFT is executed
* - Signal processing is done
* - iFFT is executed
* - The oldest FFT buffers are appended to the I/Q output queues
*
* The bin step is the sampling frequency divided by the FFT_SIZE.
* So for S_RATE=15625 and FFT_SIZE=1024 this step is 15625/1024=15.259 Hz
@ -81,63 +100,68 @@ volatile uint32_t dsp_tickx = 0; // Load indicator DSP loop
// Spectrum bins for a frequency
#define BIN(f) (int)(((f)*FFT_SIZE+S_RATE/2)/S_RATE)
#define BIN_FC 256
#define BIN_FC 256 // BIN_FC > BIN_3000 to avoid aliasing!
#define BIN_100 7
#define BIN_300 20
#define BIN_900 59
#define BIN_3000 197
/*
* This applies a bandpass filter to XI and XQ buffers
* lowbin and highbin edges must be between 3 and FFT_SIZE/2 - 3
* sign: <0 only LSB is passed
* >0 only USB is passed
* =0 LSB and USB are passed
* Edge is a 7 bin raised cosine flank, i.e. 100Hz wide
* Coefficients are: 0, 0.067, 0.25, 0.5, 0.75, 0.933, 1
* where the edge bin is in the center of this flank
* Note: maybe make slope less steep, e.g. 9 or 11 bins
* where the edge bin is in the center of this flank
* Note: maybe make slope less steep, e.g. 9 or 11 bins
*/
inline void dsp_bandpass(int lowbin, int highbin)
void __not_in_flash_func(dsp_bandpass)(int lowbin, int highbin, int sign)
{
int i;
int i, lo1, lo2, hi1, hi2;
if ((lowbin<3)||(highbin>(FFT_SIZE/2-3))||(highbin-lowbin<6)) return;
XI_buf[0] = 0; XQ_buf[0] = 0;
for (i=1; i<lowbin-2; i++)
{
XI_buf[i] = 0; XI_buf[FFT_SIZE-i] = 0;
XQ_buf[i] = 0; XQ_buf[FFT_SIZE-i] = 0;
}
for (i=highbin+3; i<FFT_SIZE-highbin-2; i++)
{
XI_buf[i] = 0;
XQ_buf[i] = 0;
}
// Boundaries are inclusive
if (sign>=0) { lo1 = lowbin-2; lo2 = highbin+2; }
if (sign<=0) { hi1 = FFT_SIZE-highbin-2; hi2 = FFT_SIZE-lowbin+2; }
// Note: There is not much difference between using or discarding Q bins
i=lowbin-2;
// Null all bins excluded from filter
for (i=1; i<lo1; i++) { XI_buf[i] = 0; XQ_buf[i] = 0; }
for (i=lo2+1; i<hi1; i++) { XI_buf[i] = 0; XQ_buf[i] = 0; }
for (i=hi2+1; i<FFT_SIZE; i++) { XI_buf[i] = 0; XQ_buf[i] = 0; }
// Calculate edges, raised cosine
i=lo1; // USB
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067; i++;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i++;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i++;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i++;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
i=highbin-2;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933; i++;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i++;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i++;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i++;
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067;
i=FFT_SIZE-highbin-2;
i=lo2;
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067; i--;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i--;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i--;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i--;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
i=hi1; // LSB
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067; i++;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i++;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i++;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i++;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
i=FFT_SIZE-lowbin-2;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933; i++;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i++;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i++;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i++;
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067;
i=hi2;
XI_buf[i] = XI_buf[i]*0.067; XQ_buf[i] = XQ_buf[i]*0.067; i--;
XI_buf[i] = XI_buf[i]*0.250; XQ_buf[i] = XQ_buf[i]*0.250; i--;
XI_buf[i] = XI_buf[i]*0.500; XQ_buf[i] = XQ_buf[i]*0.500; i--;
XI_buf[i] = XI_buf[i]*0.750; XQ_buf[i] = XQ_buf[i]*0.750; i--;
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
}
@ -145,6 +169,9 @@ inline void dsp_bandpass(int lowbin, int highbin)
/** CORE1: RX branch **/
/*
* Execute RX branch signal processing
* max time to spend is <32ms (BUFSIZE*TIM_US)
* The pre-processed I/Q samples are passed in I_BUF and Q_BUF
* The calculated A samples are passed in A_BUF
*/
volatile int scale0;
volatile int scale1;
@ -157,7 +184,7 @@ bool __not_in_flash_func(rx)(void)
b = dsp_active; // Point to Active sample buffer
/*** Copy saved I/Q buffers to FFT buffer ***/
/*** Copy saved I/Q buffers to FFT filter buffer ***/
if (++b > 2) b = 0; // Point to Old Saved sample buffer
ip = &I_buf[b][0]; xip = &XI_buf[0];
qp = &Q_buf[b][0]; xqp = &XQ_buf[0];
@ -177,16 +204,20 @@ bool __not_in_flash_func(rx)(void)
/*** Execute FFT ***/
scale0 = fix_fft(&XI_buf[0], &XQ_buf[0], false);
scale0 = fix_fft(&XI_buf[0], &XQ_buf[0], false); // Frequency domain filter input
/*** Shift and filter sidebands ***/
XI_buf[0] = 0;
XQ_buf[0] = 0;
// At this point USB and LSB surround Fc
// The desired sidebands must be shifted to their target positions around 0
// Pos USB to bin 0 and Neg USB to bin FFT_SIZE, or
// Neg LSB to bin 0 and Pos LSB to bin FFT_SIZE, or
// Pos USB to bin 0 and Pos LSB to bin FFT_SIZE
XI_buf[0] = 0; XQ_buf[0] = 0; // No DC
switch (dsp_mode)
{
case MODE_USB:
// Shift Fc to 0Hz
// Shift Fc + USB to 0Hz + USB
for (i=1; i<BIN_3000; i++)
{
XI_buf[i] = XI_buf[i+BIN_FC];
@ -195,10 +226,10 @@ bool __not_in_flash_func(rx)(void)
XQ_buf[FFT_SIZE-i] = XQ_buf[FFT_SIZE-BIN_FC-i];
}
// Bandpass DSB (2x USB)
dsp_bandpass(BIN_100, BIN_3000);
dsp_bandpass(BIN_100, BIN_3000, 0);
break;
case MODE_LSB:
// Shift Fc to 0Hz, i.e. swap buffers
// Shift Fc - LSB to 0Hz - LSB
for (i=1; i<BIN_3000; i++)
{
XI_buf[BUFSIZE-i] = XI_buf[BIN_FC-i];
@ -209,7 +240,7 @@ bool __not_in_flash_func(rx)(void)
XQ_buf[FFT_SIZE-i] = XQ_buf[BUFSIZE-i];
}
// Bandpass DSB (2x LSB)
dsp_bandpass(BIN_100, BIN_3000);
dsp_bandpass(BIN_100, BIN_3000, 0);
break;
case MODE_AM:
// Shift the rest to the right place
@ -221,7 +252,7 @@ bool __not_in_flash_func(rx)(void)
XQ_buf[i] = XQ_buf[BIN_FC+i];
}
// Bandpass DSB (LSB + USB)
dsp_bandpass(BIN_100, BIN_3000);
dsp_bandpass(BIN_100, BIN_3000, 0);
break;
case MODE_CW:
// Shift carrier from Fc to 900Hz
@ -232,8 +263,8 @@ bool __not_in_flash_func(rx)(void)
XQ_buf[i+BIN_900] = XQ_buf[BIN_FC+i];
XQ_buf[FFT_SIZE-i-BIN_900] = XQ_buf[FFT_SIZE-BIN_FC-i];
}
// Bandpass CW
dsp_bandpass(BIN_900-BIN_300, BIN_900+BIN_300);
// Bandpass CW, 600Hz
dsp_bandpass(BIN_900-BIN_300, BIN_900+BIN_300, 0);
break;
}
@ -266,19 +297,135 @@ bool __not_in_flash_func(rx)(void)
/** CORE1: TX branch **/
/*
* Execute TX branch signal processing
* max time to spend is <32ms (BUFSIZE*TIM_US)
* The pre-processed A samples are passed in A_BUF
* The calculated I and Q samples are passed in I_BUF and Q_BUF
*/
bool __not_in_flash_func(tx)(void)
{
// Export FFT buffers to I/Q
int b;
int i;
int16_t *ip, *qp, *ap, *xip, *xqp;
int16_t peak;
b = dsp_active; // Point to Active sample buffer
// Import A buffers
/*** Copy saved A buffers to FFT buffers, NULL Im. part ***/
if (++b > 2) b = 0; // Point to Old Saved sample buffer
ap = &A_buf[b][0]; xip = &XI_buf[0];
xqp = &XQ_buf[0];
for (i=0; i<BUFSIZE; i++)
{
*xip++ = *ip++;
*xqp++ = 0;
}
if (++b > 2) b = 0; // Point to New Saved sample buffer
ap = &A_buf[b][0]; xip = &XI_buf[BUFSIZE];
xqp = &XQ_buf[BUFSIZE];
for (i=0; i<BUFSIZE; i++)
{
*xip++ = *ip++;
*xqp++ = 0;
}
// FFT
/*** Execute FFT ***/
scale0 = fix_fft(&XI_buf[0], &XQ_buf[0], false);
// Filter
// iFFT
/*** Shift and filter sidebands ***/
XI_buf[0] = 0; XQ_buf[0] = 0; // No DC
switch (dsp_mode)
{
case MODE_USB:
// Bandpass Audio, USB only
dsp_bandpass(BIN_100, BIN_3000, 1);
// Shift USB up to to Fc, assumes Fc > bandwidth
for (i=1; i<BIN_3000; i++)
{
XI_buf[BIN_FC+i] = XI_buf[i];
XQ_buf[BIN_FC+i] = XQ_buf[i];
XI_buf[i] = 0;
XQ_buf[i] = 0;
}
for (i=1; i<BIN_3000; i++)
{
XI_buf[FFT_SIZE-BIN_FC-i] = XI_buf[BIN_FC+i];
XQ_buf[FFT_SIZE-BIN_FC-i] = XQ_buf[BIN_FC+i];
}
break;
case MODE_LSB:
// Bandpass Audio, LSB only
dsp_bandpass(BIN_100, BIN_3000, -1);
// Shift LSB up to Fc
for (i=1; i<BIN_3000; i++)
{
XI_buf[BIN_FC-i] = XI_buf[FFT_SIZE-i];
XQ_buf[BIN_FC-i] = XQ_buf[FFT_SIZE-i];
XI_buf[FFT_SIZE-i] = 0;
XQ_buf[FFT_SIZE-i] = 0;
}
for (i=1; i<BIN_3000; i++)
{
XI_buf[FFT_SIZE-BIN_FC+i] = XI_buf[BIN_FC-i];
XQ_buf[FFT_SIZE-BIN_FC+i] = XQ_buf[BIN_FC-i];
}
break;
case MODE_AM:
// Bandpass Audio
dsp_bandpass(BIN_100, BIN_3000, 0);
// Shift DSB up to Fc
for (i=1; i<BIN_3000; i++)
{
XI_buf[BIN_FC+i] = XI_buf[i];
XQ_buf[BIN_FC+i] = XQ_buf[i];
XI_buf[i] = 0;
XQ_buf[i] = 0;
XI_buf[BIN_FC-i] = XI_buf[FFT_SIZE-i];
XQ_buf[BIN_FC-i] = XQ_buf[FFT_SIZE-i];
XI_buf[FFT_SIZE-i] = 0;
XQ_buf[FFT_SIZE-i] = 0;
}
for (i=1; i<BIN_3000; i++)
{
XI_buf[FFT_SIZE-BIN_FC-i] = XI_buf[BIN_FC+i];
XQ_buf[FFT_SIZE-BIN_FC-i] = XQ_buf[BIN_FC+i];
XI_buf[FFT_SIZE-BIN_FC+i] = XI_buf[BIN_FC-i];
XQ_buf[FFT_SIZE-BIN_FC+i] = XQ_buf[BIN_FC-i];
}
break;
case MODE_CW:
// Create a carrier on 900Hz from Fc
break;
}
/*** Execute inverse FFT ***/
scale1 = fix_fft(&XI_buf[0], &XQ_buf[0], true);
/*** Export FFT buffer to I and Q ***/
b = dsp_active; // Assume active buffer not changed, i.e. no overruns
if (++b > 2) b = 0; // Point to oldest (will be next for output)
qp = &Q_buf[b][0]; xqp = &XQ_buf[BUFSIZE];
ip = &I_buf[b][0]; xip = &XI_buf[BUFSIZE];
for (i=0; i<BUFSIZE; i++)
{
*qp++ = *xqp++; // Copy newest results
*ip++ = *xip++; // Copy newest results
}
/*** Scale down into DAC_RANGE! ***/
peak = 256;
for (i=0; i<BUFSIZE; i++)
{
Q_buf[b][i] /= peak;
I_buf[b][i] /= peak;
}
return true;
}

Wyświetl plik

@ -30,6 +30,10 @@
#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
@ -49,30 +53,28 @@ int16_t lpf15_62[15] = { -1, 3, 12, 6,-12, -4, 40, 69, 40, -4,-12, 6, 12, 3,
/*
* 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 uint16_t peak=0; // Peak detector running value
volatile int16_t agc_gain=0; // AGC gain (left-shift value)
volatile int16_t agc_accu=0; // Log peak level integrator
volatile int32_t i_s[15], q_s[15]; // Filtered I/Q samples
volatile int32_t i2, q2; // Squared samples
bool __not_in_flash_func(rx)(void)
{
int32_t q_accu, i_accu;
int32_t qh;
uint16_t i;
int16_t k;
/*** SAMPLING ***/
/*
* Shift-in I and Q raw samples
*/
for (i=0; i<14; i++)
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; // Store decimated samples in shift registers
q_s_raw[14] = q_sample;
i_s_raw[14] = i_sample;
@ -89,7 +91,7 @@ bool __not_in_flash_func(rx)(void)
q_accu = q_accu/256;
i_accu = i_accu/256;
for (i=0; i<14; i++) // Shift filtered samples
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];
@ -153,11 +155,12 @@ bool __not_in_flash_func(rx)(void)
/** CORE1: TX branch **/
/*
* Execute TX branch signal processing,
* max time to spend is <16us, i.e. rate is 62.5 kHz
* The audio sampling has already been done in vox()
* 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 a_dc; // DC level
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;