kopia lustrzana https://github.com/ArjanteMarvelde/uSDR-pico
rodzic
a712c1c29b
commit
f934f9ccb0
76
dsp.c
76
dsp.c
|
@ -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 ***/
|
/*** Include the desired DSP engine ***/
|
||||||
|
|
||||||
#if DSP_FFT == 1
|
#if DSP_FFT == 1
|
||||||
|
@ -229,17 +227,18 @@ volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample
|
||||||
/** CORE1: ADC IRQ handler **/
|
/** CORE1: ADC IRQ handler **/
|
||||||
/*
|
/*
|
||||||
* The IRQ handling is redirected to a DMA channel
|
* 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 LSH 8 // Left shift for higher accuracy of level, also LPF
|
||||||
#define ADC_LEVELS (ADC_BIAS/2)<<LSH // Shifted initial ADC level
|
#define ADC_LEVELS (ADC_BIAS/2)<<LSH // Left shifted initial ADC level value
|
||||||
#define BSH 8 // Shift for higher accuracy of bias, also LPF
|
#define BSH 8 // Left shift for higher accuracy of bias, also LPF
|
||||||
#define ADC_BIASS ADC_BIAS<<BSH // Shifted initial ADC bias
|
#define ADC_BIASS ADC_BIAS<<BSH // Left shifted initial ADC bias value
|
||||||
#define ADC_INT 8 // Nr of samples for integration (max 8)
|
#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 samples collection
|
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 level
|
volatile int32_t adc_bias[3] = {ADC_BIASS, ADC_BIASS, ADC_BIASS}; // ADC dynamic bias (DC) level
|
||||||
volatile int32_t adc_result[3]; // ADC filtered result for further processing
|
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}; // Levels for ADC channels
|
volatile uint32_t adc_level[3] = {ADC_LEVELS, ADC_LEVELS, ADC_LEVELS}; // Signa levels for ADC channels
|
||||||
volatile int adccnt = 0; // Sampling overflow indicator
|
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;
|
int32_t temp;
|
||||||
|
|
||||||
/*
|
/** Here the rate is: S_RATE=1/TIM_US, assume 15625Hz **/
|
||||||
* Here the rate is 15625Hz
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get samples and correct for DC bias
|
// Get ADC_INT samples for each channel and correct for DC bias
|
||||||
// RC: ((1<<BSH)-1)*64usec = 16msec
|
// 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[0] = 0;
|
||||||
adc_result[1] = 0;
|
adc_result[1] = 0;
|
||||||
adc_result[2] = 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);
|
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
|
adccnt--; // ADC overrun indicator decrement
|
||||||
adc_select_input(0); // Start with ADC0
|
adc_select_input(0); // Start with ADC0
|
||||||
while (!adc_fifo_is_empty()) adc_fifo_get(); // Empty leftovers from fifo, if any
|
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].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].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].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
|
// Calculate and save signal level, value is left shifted by LSH = 8
|
||||||
// RC: ((1<<LSH)-1)*64usec = 16msec
|
// LPF RC: ((1<<LSH)-1)*64usec = 16msec
|
||||||
adc_level[0] += (ABS(adc_result[0]))-(adc_level[0]>>LSH);
|
adc_level[0] += (ABS(adc_result[0]))-(adc_level[0]>>LSH);
|
||||||
adc_level[1] += (ABS(adc_result[1]))-(adc_level[1]>>LSH);
|
adc_level[1] += (ABS(adc_result[1]))-(adc_level[1]>>LSH);
|
||||||
adc_level[2] += (ABS(adc_result[2]))-(adc_level[2]>>LSH);
|
adc_level[2] += (ABS(adc_result[2]))-(adc_level[2]>>LSH);
|
||||||
|
|
||||||
// Derive RSSI value from RX vector length
|
// Derive RSSI value from RX vector length
|
||||||
// Crude AGC mechanism **TO BE IMPROVED**
|
// Crude AGC mechanism **NEEDS TO BE IMPROVED**
|
||||||
if (!tx_enabled)
|
if (!tx_enabled)
|
||||||
{
|
{
|
||||||
// Approximate amplitude, with alpha max + beta min function
|
// 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
|
#if DSP_FFT == 1
|
||||||
|
|
||||||
|
// Copy samples from/to the right buffers
|
||||||
if (tx_enabled)
|
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_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
|
pwm_set_gpio_level(DAC_Q, Q_buf[dsp_active][dsp_tick] + DAC_BIAS); // Output Q to DAC
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
I_buf[dsp_active][dsp_tick] = (int16_t)(rx_agc*adc_result[1]);
|
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]);
|
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
|
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
|
if (++dsp_tick >= BUFSIZE) // Increment tick and check range
|
||||||
{
|
{
|
||||||
dsp_tick = 0; // Reset counter
|
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
|
dsp_overrun++; // Increment overrun counter
|
||||||
sem_release(&dsp_sem); // Signal DSP loop semaphore
|
sem_release(&dsp_sem); // Signal background processing
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
// Copy samples from/to the right buffers
|
||||||
if (tx_enabled)
|
if (tx_enabled)
|
||||||
{
|
{
|
||||||
a_sample = tx_agc * adc_result[2]; // Store A for DSP use
|
a_sample = tx_agc * adc_result[2]; // Store A sample for background processing
|
||||||
pwm_set_gpio_level(DAC_I, i_sample); // Output I to DAC
|
pwm_set_gpio_level(DAC_I, i_sample); // Output calculated I sample to DAC
|
||||||
pwm_set_gpio_level(DAC_Q, q_sample); // Output Q to DAC
|
pwm_set_gpio_level(DAC_Q, q_sample); // Output calculated Q sample to DAC
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pwm_set_gpio_level(DAC_A, a_sample); // Output Q to DAC
|
q_sample = rx_agc * adc_result[0]; // Store Q sample for background processing
|
||||||
q_sample = rx_agc * adc_result[0]; // Store Q for DSP use
|
i_sample = rx_agc * adc_result[1]; // Store I sample for background processing
|
||||||
i_sample = rx_agc * adc_result[1]; // Store I for DSP use
|
pwm_set_gpio_level(DAC_A, a_sample); // Output calculated A sample to DAC
|
||||||
}
|
}
|
||||||
dsp_overrun++; // Increment overrun counter
|
dsp_overrun++; // Increment overrun counter
|
||||||
sem_release(&dsp_sem); // Signal DSP loop semaphore
|
sem_release(&dsp_sem); // Signal background processing
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -475,9 +476,10 @@ void __not_in_flash_func(dsp_loop)()
|
||||||
|
|
||||||
dsp_overrun = 0;
|
dsp_overrun = 0;
|
||||||
|
|
||||||
|
// Background processing loop
|
||||||
while(1)
|
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
|
dsp_overrun--; // Decrement overrun counter
|
||||||
|
|
||||||
// Use adc_level[2] for VOX
|
// Use adc_level[2] for VOX
|
||||||
|
|
255
dsp_fft.c
255
dsp_fft.c
|
@ -26,26 +26,45 @@
|
||||||
* The other two are swapped with the FFT signal processing buffers.
|
* The other two are swapped with the FFT signal processing buffers.
|
||||||
* Since we use complex FFT, the algorithm uses 4x 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 --> | | | |
|
* i --> | | | | | | | | --> a
|
||||||
* +--+--+--+
|
* +--+--+--+ +--+--+--+
|
||||||
* \ \ \ +--+--+
|
* \ \ \ +--+--+ / /
|
||||||
* --------> | | | +--+--+--+
|
* ---------> | | | -------
|
||||||
* +--+--+ FFT-DSP-iFFT --> | | | | --> a
|
* +--+--+ FFT-DSP-iFFT
|
||||||
* --------> | | | +--+--+--+
|
* ---------> | | |
|
||||||
* / / / +--+--+
|
* / / / +--+--+
|
||||||
* +--+--+--+
|
* +--+--+--+
|
||||||
* q --> | | | |
|
* q --> | | | |
|
||||||
* +--+--+--+
|
* +--+--+--+
|
||||||
*
|
*
|
||||||
* RX, when triggered by timer callback:
|
* 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
|
* - The oldest two I and Q buffers are copied into the FFT buffers
|
||||||
* - FFT is executed
|
* - FFT is executed
|
||||||
* - Signal processing is done
|
* - Signal processing is done
|
||||||
* - iFFT is executed
|
* - 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.
|
* 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
|
* 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
|
// Spectrum bins for a frequency
|
||||||
#define BIN(f) (int)(((f)*FFT_SIZE+S_RATE/2)/S_RATE)
|
#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_100 7
|
||||||
#define BIN_300 20
|
#define BIN_300 20
|
||||||
#define BIN_900 59
|
#define BIN_900 59
|
||||||
#define BIN_3000 197
|
#define BIN_3000 197
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This applies a bandpass filter to XI and XQ buffers
|
* This applies a bandpass filter to XI and XQ buffers
|
||||||
* lowbin and highbin edges must be between 3 and FFT_SIZE/2 - 3
|
* 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
|
* 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
|
* 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
|
* where the edge bin is in the center of this flank
|
||||||
* Note: maybe make slope less steep, e.g. 9 or 11 bins
|
* 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;
|
if ((lowbin<3)||(highbin>(FFT_SIZE/2-3))||(highbin-lowbin<6)) return;
|
||||||
|
|
||||||
XI_buf[0] = 0; XQ_buf[0] = 0;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: There is not much difference between using or discarding Q bins
|
// Boundaries are inclusive
|
||||||
i=lowbin-2;
|
if (sign>=0) { lo1 = lowbin-2; lo2 = highbin+2; }
|
||||||
|
if (sign<=0) { hi1 = FFT_SIZE-highbin-2; hi2 = FFT_SIZE-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.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.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.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.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;
|
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
|
||||||
i=highbin-2;
|
i=lo2;
|
||||||
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933; i++;
|
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.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.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.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;
|
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
|
||||||
i=FFT_SIZE-highbin-2;
|
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.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.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.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.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;
|
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933;
|
||||||
i=FFT_SIZE-lowbin-2;
|
i=hi2;
|
||||||
XI_buf[i] = XI_buf[i]*0.933; XQ_buf[i] = XQ_buf[i]*0.933; i++;
|
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.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.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.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;
|
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 **/
|
/** CORE1: RX branch **/
|
||||||
/*
|
/*
|
||||||
* Execute RX branch signal processing
|
* 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 scale0;
|
||||||
volatile int scale1;
|
volatile int scale1;
|
||||||
|
@ -157,7 +184,7 @@ bool __not_in_flash_func(rx)(void)
|
||||||
|
|
||||||
b = dsp_active; // Point to Active sample buffer
|
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
|
if (++b > 2) b = 0; // Point to Old Saved sample buffer
|
||||||
ip = &I_buf[b][0]; xip = &XI_buf[0];
|
ip = &I_buf[b][0]; xip = &XI_buf[0];
|
||||||
qp = &Q_buf[b][0]; xqp = &XQ_buf[0];
|
qp = &Q_buf[b][0]; xqp = &XQ_buf[0];
|
||||||
|
@ -177,16 +204,20 @@ bool __not_in_flash_func(rx)(void)
|
||||||
|
|
||||||
|
|
||||||
/*** Execute FFT ***/
|
/*** 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 ***/
|
/*** Shift and filter sidebands ***/
|
||||||
XI_buf[0] = 0;
|
// At this point USB and LSB surround Fc
|
||||||
XQ_buf[0] = 0;
|
// 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)
|
switch (dsp_mode)
|
||||||
{
|
{
|
||||||
case MODE_USB:
|
case MODE_USB:
|
||||||
// Shift Fc to 0Hz
|
// Shift Fc + USB to 0Hz + USB
|
||||||
for (i=1; i<BIN_3000; i++)
|
for (i=1; i<BIN_3000; i++)
|
||||||
{
|
{
|
||||||
XI_buf[i] = XI_buf[i+BIN_FC];
|
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];
|
XQ_buf[FFT_SIZE-i] = XQ_buf[FFT_SIZE-BIN_FC-i];
|
||||||
}
|
}
|
||||||
// Bandpass DSB (2x USB)
|
// Bandpass DSB (2x USB)
|
||||||
dsp_bandpass(BIN_100, BIN_3000);
|
dsp_bandpass(BIN_100, BIN_3000, 0);
|
||||||
break;
|
break;
|
||||||
case MODE_LSB:
|
case MODE_LSB:
|
||||||
// Shift Fc to 0Hz, i.e. swap buffers
|
// Shift Fc - LSB to 0Hz - LSB
|
||||||
for (i=1; i<BIN_3000; i++)
|
for (i=1; i<BIN_3000; i++)
|
||||||
{
|
{
|
||||||
XI_buf[BUFSIZE-i] = XI_buf[BIN_FC-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];
|
XQ_buf[FFT_SIZE-i] = XQ_buf[BUFSIZE-i];
|
||||||
}
|
}
|
||||||
// Bandpass DSB (2x LSB)
|
// Bandpass DSB (2x LSB)
|
||||||
dsp_bandpass(BIN_100, BIN_3000);
|
dsp_bandpass(BIN_100, BIN_3000, 0);
|
||||||
break;
|
break;
|
||||||
case MODE_AM:
|
case MODE_AM:
|
||||||
// Shift the rest to the right place
|
// 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];
|
XQ_buf[i] = XQ_buf[BIN_FC+i];
|
||||||
}
|
}
|
||||||
// Bandpass DSB (LSB + USB)
|
// Bandpass DSB (LSB + USB)
|
||||||
dsp_bandpass(BIN_100, BIN_3000);
|
dsp_bandpass(BIN_100, BIN_3000, 0);
|
||||||
break;
|
break;
|
||||||
case MODE_CW:
|
case MODE_CW:
|
||||||
// Shift carrier from Fc to 900Hz
|
// 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[i+BIN_900] = XQ_buf[BIN_FC+i];
|
||||||
XQ_buf[FFT_SIZE-i-BIN_900] = XQ_buf[FFT_SIZE-BIN_FC-i];
|
XQ_buf[FFT_SIZE-i-BIN_900] = XQ_buf[FFT_SIZE-BIN_FC-i];
|
||||||
}
|
}
|
||||||
// Bandpass CW
|
// Bandpass CW, 600Hz
|
||||||
dsp_bandpass(BIN_900-BIN_300, BIN_900+BIN_300);
|
dsp_bandpass(BIN_900-BIN_300, BIN_900+BIN_300, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,18 +297,134 @@ bool __not_in_flash_func(rx)(void)
|
||||||
/** CORE1: TX branch **/
|
/** CORE1: TX branch **/
|
||||||
/*
|
/*
|
||||||
* Execute TX branch signal processing
|
* 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)
|
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;
|
||||||
|
|
||||||
// Import A buffers
|
b = dsp_active; // Point to Active sample buffer
|
||||||
|
|
||||||
// FFT
|
/*** 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Filter
|
|
||||||
|
|
||||||
// iFFT
|
/*** Execute FFT ***/
|
||||||
|
scale0 = fix_fft(&XI_buf[0], &XQ_buf[0], false);
|
||||||
|
|
||||||
|
|
||||||
|
/*** 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
25
dsp_tim.c
25
dsp_tim.c
|
@ -30,6 +30,10 @@
|
||||||
|
|
||||||
#include "uSDR.h"
|
#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/)
|
* 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
|
* 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
|
* 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_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 i_s[15], q_s[15]; // Filtered I/Q samples
|
||||||
volatile int32_t i2, q2; // Squared samples
|
|
||||||
bool __not_in_flash_func(rx)(void)
|
bool __not_in_flash_func(rx)(void)
|
||||||
{
|
{
|
||||||
int32_t q_accu, i_accu;
|
int32_t q_accu, i_accu;
|
||||||
int32_t qh;
|
int32_t qh;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
int16_t k;
|
|
||||||
|
|
||||||
/*** SAMPLING ***/
|
/*** SAMPLING ***/
|
||||||
/*
|
/*
|
||||||
* Shift-in I and Q raw samples
|
* 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];
|
q_s_raw[i] = q_s_raw[i+1];
|
||||||
i_s_raw[i] = i_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;
|
i_s_raw[14] = i_sample;
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +91,7 @@ bool __not_in_flash_func(rx)(void)
|
||||||
q_accu = q_accu/256;
|
q_accu = q_accu/256;
|
||||||
i_accu = i_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];
|
q_s[i] = q_s[i+1];
|
||||||
i_s[i] = i_s[i+1];
|
i_s[i] = i_s[i+1];
|
||||||
|
@ -153,11 +155,12 @@ bool __not_in_flash_func(rx)(void)
|
||||||
/** CORE1: TX branch **/
|
/** CORE1: TX branch **/
|
||||||
/*
|
/*
|
||||||
* Execute TX branch signal processing,
|
* Execute TX branch signal processing,
|
||||||
* max time to spend is <16us, i.e. rate is 62.5 kHz
|
* max time to spend is <64us (TIM_US)
|
||||||
* The audio sampling has already been done in vox()
|
* 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_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)
|
bool __not_in_flash_func(tx)(void)
|
||||||
{
|
{
|
||||||
int32_t a_accu, q_accu;
|
int32_t a_accu, q_accu;
|
||||||
|
|
Ładowanie…
Reference in New Issue