diff --git a/dsp.c b/dsp.c index c9d4cf7..d24a9d3 100644 --- a/dsp.c +++ b/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 ***/ #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)<>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); 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 diff --git a/dsp_fft.c b/dsp_fft.c index adaeb3a..12aa802 100644 --- a/dsp_fft.c +++ b/dsp_fft.c @@ -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=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 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 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 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 bandwidth + for (i=1; i 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