From 9e4cda73a0f6e2a398a26b2970a5242260113441 Mon Sep 17 00:00:00 2001 From: ArjanteMarvelde Date: Wed, 13 Oct 2021 17:19:07 +0200 Subject: [PATCH] AGC added --- dsp.c | 196 +++++++++++++++++++++++++++++++++++++------------------ hmi.c | 2 +- si5351.c | 9 ++- 3 files changed, 139 insertions(+), 68 deletions(-) diff --git a/dsp.c b/dsp.c index 7a03033..7d3eb85 100644 --- a/dsp.c +++ b/dsp.c @@ -46,10 +46,10 @@ * ADC is 12 bit, so resolution is by definition 4096 * To eliminate undefined behavior we clip off the upper 4 sample bits. */ -#define DAC_RANGE 250 -#define DAC_BIAS DAC_RANGE/2 +#define DAC_RANGE 256 +#define DAC_BIAS (DAC_RANGE/2) #define ADC_RANGE 4096 -#define ADC_BIAS ADC_RANGE/2 +#define ADC_BIAS (ADC_RANGE/2) /* * Callback timeout and inter-core FIFO commands. @@ -62,9 +62,18 @@ #define DSP_RX 2 +/* + * AGC reference level is log2(64) = 6, where 64 is the MSB of half DAC_RANGE + * 1/AGC_DECAY and 1/AGC_ATTACK are multipliers before agc_gain value integrator + * These values should ultimately be set by the HMI. + */ +#define AGC_REF 6 +#define AGC_DECAY 1024 +#define AGC_ATTACK 128 + /* * Low pass filters Fc=3, 7 and 15 kHz (see http://t-filter.engineerjs.com/) - * for sample rates 62.500 , 31.250 or 15.625 kHz , stopband is appr -40dB + * Settings: sample rates 62500, 31250 or 15625 Hz, stopband -40dB, passband ripple 5dB * Note: 8 bit precision, so divide sum by 256 */ 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 @@ -79,6 +88,12 @@ volatile uint32_t fifo_overrun, fifo_rx, fifo_tx, fifo_xx, fifo_incnt; volatile bool tx_enabled; +/* + * Some macro's + * See Alpha Max plus Beta Min algorithm for MAG (vector length) + */ +#define ABS(x) ((x)<0?-(x):(x)) +#define MAG(i,q) (ABS(i)>ABS(q) ? ABS(i)+((3*ABS(q))>>3) : ABS(q)+((3*ABS(i))>>3)) /* @@ -88,61 +103,85 @@ volatile bool tx_enabled; */ volatile uint16_t adc_result[3]; volatile int adc_next; // Remember which ADC the result is from -uint32_t adc_count=0; +uint32_t adc_count=0; // Debugging void adcfifo_handler(void) { - adc_result[adc_next] = adc_fifo_get(); - adc_next = adc_hw->cs; - adc_next = (adc_next >> ADC_CS_AINSEL_LSB) & 3; // Only 0, 1 and are valid + adc_result[adc_next] = adc_fifo_get(); // Get result from fifo + adc_next = adc_hw->cs; // Update adc_next with HW CS register + adc_next = (adc_next >> ADC_CS_AINSEL_LSB) & 3; // Shift and Mask: Only 0, 1 and 2 are valid numbers adc_count++; } /* * CORE1: - * Execute RX branch signal processing, max time to spend is <16us ! - * No ADC sample interleaving, take both I and Q channels. + * Execute RX branch signal processing, max time to spend is <16us, i.e. rate is 62.5 kHz + * No ADC sample interleaving, read both I and Q channels. * The delay is only 2us per conversion, which causes less distortion than interpolation of samples. */ volatile int16_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_accu=0; // AGC gain (shift value), log peak level integrator volatile int16_t i_s[15], q_s[15]; // Filtered I/Q samples volatile int16_t i_dc, q_dc; // DC bias for I/Q channel volatile int rx_cnt=0; // Decimation counter bool rx(void) { - int16_t q_sample, i_sample; + int16_t q_sample, i_sample, a_sample; int32_t q_accu, i_accu; int16_t qh; - int i; + uint16_t i; + int16_t k; + /*** SAMPLING ***/ + i_sample = adc_result[0]; // Take last ADC 0 result q_sample = adc_result[1]; // Take last ADC 1 result + + /* + * Remove DC and store new sample + * IIR filter: dc = a*sample + (1-a)*dc where a = 1/128 + * Amplitude of samples should fit inside [-2048, 2047] + */ + q_sample = (q_sample&0x0fff) - ADC_BIAS; // Clip to 12 bits and subtract mid-range + q_dc += (q_sample>>7) - (q_dc>>7); // then IIR running average + q_sample -= q_dc; // and subtract DC + i_sample = (i_sample&0x0fff) - ADC_BIAS; // Same for I sample + i_dc += (i_sample>>7) - (i_dc>>7); + i_sample -= i_dc; + /* + * Shift with AGC feedback from AUDIO GENERATION stage + * This behavior in essence is exponential, complementing the logarithmic peak detector + */ + if (agc_gain > 0) + { + q_sample <<= agc_gain; + i_sample <<= agc_gain; + } + else if (agc_gain < 0) + { + q_sample >>= -agc_gain; + i_sample >>= -agc_gain; + } + /* - * Shift in I and Q raw samples + * Shift-in I and Q raw samples */ for (i=0; i<14; i++) { q_s_raw[i] = q_s_raw[i+1]; // Q raw samples shift register i_s_raw[i] = i_s_raw[i+1]; // I raw samples shift register } - - /* - * Remove DC and store new sample - * IIR filter: dc = a*sample + (1-a)*dc where a = 1/128 - */ - q_sample = (q_sample&0x0fff) - ADC_BIAS; // Clip and subtract mid-range - q_dc += (q_sample>>7) - (q_dc>>7); // then IIR running average - q_s_raw[14] = q_sample - q_dc; // and subtract DC, store in shift register - i_sample = (i_sample&0x0fff) - ADC_BIAS; - i_dc += (i_sample>>7) - (i_dc>>7); - i_s_raw[14] = i_sample - i_dc; - + q_s_raw[14] = q_sample; // Store in shift registers + i_s_raw[14] = i_sample; + + /* * Low pass filter + decimation */ rx_cnt = (rx_cnt+1)&3; // Calculate only every fourth sample - if (rx_cnt>0) return true; + if (rx_cnt>0) return (true); // So net sample time is 64us or 15.625 kHz for (i=0; i<14; i++) // Shift decimated samples { @@ -153,50 +192,68 @@ bool rx(void) i_accu = 0; for (i=0; i<15; i++) // Low pass FIR filter { - q_accu += (int32_t)q_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz sampling - i_accu += (int32_t)i_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz sampling + q_accu += (int32_t)q_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling + i_accu += (int32_t)i_s_raw[i]*lpf3_62[i]; // Fc=3kHz, at 62.5 kHz raw sampling } - q_s[14] = q_accu >> 8; - i_s[14] = q_accu >> 8; + q_accu >>= 8; + i_accu >>= 8; + + q_s[14] = q_accu; + i_s[14] = i_accu; + - /* - * From here things get dependent on receive mode. - * We assume USB for now, e.g. supporting FT8 - */ + /*** DEMODULATION ***/ /* - * Classic Hilbert transform 15 taps, 12 bits (see Iowa Hills) + * 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 >> 12; + qh = q_accu >> 12; + a_sample = i_s[7] - qh; - /* - * SSB demodulate: I[7] - Qh - * Add DAC_BIAS offset, Clip to DAC_RANGE and send to audio DAC output + /* + * AM demodulate: sqrt(sqr(i)+sqr(q)) + * Approximated with MAG(i,q) */ - q_sample = ((i_s[7] - qh)/2)+DAC_BIAS; - q_sample = (q_sample>DAC_RANGE)?DAC_RANGE:((q_sample<0)?0:q_sample); - pwm_set_chan_level(dac_audio, PWM_CHAN_A, q_sample); + // a_sample = MAG(i_s[14], q_s[14]); + + + /*** AUDIO GENERATION ***/ + /* + * AGC, peak detector + * Sample speed is still 15625 per second + */ + peak = (127*peak + (ABS(a_sample)))>>7; // Running average level detect, a=1/128 + k=0; i=peak; // Logarithmic peak detection + if (i&0xff00) {k+=8; i>>=8;} // k=log2(peak), find highest bit set + if (i&0x00f0) {k+=4; i>>=4;} + if (i&0x000c) {k+=2; i>>=2;} + if (i&0x0002) {k+=1;} + agc_accu += (k - AGC_REF); // Add difference with target to integrator (Acc += Xn - R) + if (agc_accu > AGC_ATTACK) // Attack time, gain correction in case of high level + { + agc_gain--; // Decrease gain + agc_accu -= AGC_ATTACK; // Reset integrator + } else if (agc_accu < -(AGC_DECAY)) // Decay time, gain correction in case of low level + { + agc_gain++; // Increase gain + agc_accu += AGC_DECAY; // Reset integrator + } + + + /* + * Scale and clip output, + * Send to audio DAC output + */ + a_sample += DAC_BIAS; // Add bias level + if (a_sample > DAC_RANGE) // Clip to DAC range + a_sample = DAC_RANGE; + else if (a_sample<0) + a_sample = 0; + pwm_set_chan_level(dac_audio, PWM_CHAN_A, a_sample); -/* AGC ALGORITHM -if(m_Peak>m_AttackAve) //if power is rising (use m_AttackRiseAlpha time constant) - m_AttackAve = (1.0-m_AttackRiseAlpha)*m_AttackAve + m_AttackRiseAlpha*m_Peak; -else //else magnitude is falling (use m_AttackFallAlpha time constant) - m_AttackAve = (1.0-m_AttackFallAlpha)*m_AttackAve + m_AttackFallAlpha*m_Peak; -if(m_Peak>m_DecayAve) //if magnitude is rising (use m_DecayRiseAlpha time constant) -{ - m_DecayAve = (1.0-m_DecayRiseAlpha)*m_DecayAve + m_DecayRiseAlpha*m_Peak; - m_HangTimer = 0; //reset hang timer -} -else -{ //here if decreasing signal -if(m_HangTimerpriority = BUSCTRL_BUS_PRIORITY_PROC1_BITS; // Set Core 1 prio high */ void dsp_init() { diff --git a/hmi.c b/hmi.c index bceb756..999ddaa 100644 --- a/hmi.c +++ b/hmi.c @@ -310,7 +310,7 @@ void hmi_evaluate(void) { char s[20]; - sprintf(s, "%s %7.1f %c%3d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, (tx_enabled?126:127),920); + sprintf(s, "%s %7.1f %c%3d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, (tx_enabled?'T':'R'),920); lcd_writexy(0,0,s); switch (hmi_state) { diff --git a/si5351.c b/si5351.c index f10a343..d232159 100644 --- a/si5351.c +++ b/si5351.c @@ -53,8 +53,11 @@ MSi: target for mid-band, i.e. Fvco=750MHz MSN = MSi*Ri*Fout/Fxtal (should be between 24 and 36) Only use MSi even-integers, i.e. d=[4, 6, 8..126], e=0 and f=100000, and set INT bits in reg 22, 23. -Phase offset MS0 (reg 165) must be 0, MS1 (reg 166) must be equal to MS1 for 90 deg. Set INV bit in reg 17 to add 180 deg. -NOTE: Phase offsets only work when Ri = 1, this means minimum Fout is 4.762MHz at Fvco = 600MHz +Quadrature Phase offsets (i.e. delay): +- Offset for MS0 (reg 165) must be 0 (cosine), +- Offset for MS1 (reg 166) must be equal to divider MS1 for 90 deg (sine), +- Set INV bit (reg 17) to add 180 deg. +NOTE: Phase offsets only work when Ri = 1, this means minimum Fout is 4.762MHz at Fvco = 600MHz. Additional flip/flop dividers are needed to get 80m band frequencies, or Fvco must be tuned below spec. @@ -265,7 +268,7 @@ void si_setmsi(uint8_t i) data[1] = vfo[0].msi; i2c_write_blocking(i2c1, I2C_VFO, data, 2, false); } - else + else // Phase is 0 or 180 deg { data[0] = SI_CLK1_PHOFF; data[1] = 0;