Porównaj commity

...

11 Commity

Autor SHA1 Wiadomość Data
ArjanteMarvelde de115a043c V3.02 2022-07-14 21:57:21 +02:00
Arjan te Marvelde d0cd45c016
Update README.md 2022-07-14 21:25:15 +02:00
Arjan te Marvelde 7deba0ab90
Update README.md 2022-07-14 21:23:43 +02:00
ArjanteMarvelde 073caa8ade Merge branch 'main' of https://github.com/ArjanteMarvelde/uSDR-pico 2022-07-14 21:21:47 +02:00
ArjanteMarvelde 567ee9a28d Reorganize 2022-07-14 21:21:32 +02:00
Arjan te Marvelde d8c99f7eaa
Delete uSDR-I2CGEN.uf2 2022-07-14 21:19:35 +02:00
Arjan te Marvelde 544779212c
Delete uSDR-I2CADA.uf2 2022-07-14 21:19:27 +02:00
Arjan te Marvelde 4e4a9820d6
Delete uSDR-Grove.uf2 2022-07-14 21:19:13 +02:00
Arjan te Marvelde 571e63d9c0
Update README.md 2022-07-14 21:17:18 +02:00
ArjanteMarvelde b70d789d8b Merge branch 'main' of https://github.com/ArjanteMarvelde/uSDR-pico 2022-07-14 21:15:18 +02:00
ArjanteMarvelde 27486b29d8 V3.02
Added S-meter
Minor corrections
2022-07-14 21:15:09 +02:00
14 zmienionych plików z 103 dodań i 41 usunięć

Wyświetl plik

@ -3,7 +3,7 @@ This Git repository contains a Micro-SDR implementation, based on a RP2040 Pi Pi
Furthermore, the repository contains the electronic design of some modules that cover the mixing, filtering and RF amplification. Furthermore, the repository contains the electronic design of some modules that cover the mixing, filtering and RF amplification.
The ZIP files contain a consistent package, but the latest code with all the bug fixes and some new features is in the files in the main directory. The ZIP files contain a consistent package, but the latest code with all the bug fixes and some new features is in the files in the main directory.
The V3.00 package contains *two signal processing engines*, selectable with a compile switch in dsp.h. The first engine is the old time domain processor, more or less as in V2, and the second engine is a new FFT-based processor. Starting with the V3.00 package **uSDR-pico** contains *two signal processing engines*, selectable with a compile switch in dsp.h. The first engine is the old time domain processor, more or less as in V2.00, and the second engine is a new FFT-based processor.
For a more detailed description of the software and the hardware, please refer to the elaborate documentation. For a more detailed description of the software and the hardware, please refer to the elaborate documentation.
The platform used is a Pi Pico module with an RP2040 processor. This processor has dual cores, running default at 125MHz each, and a very configurable I/O which eases the HW design. The platform can be overclocked, but some functions seem to become unstable when pushed too far. The platform used is a Pi Pico module with an RP2040 processor. This processor has dual cores, running default at 125MHz each, and a very configurable I/O which eases the HW design. The platform can be overclocked, but some functions seem to become unstable when pushed too far.
@ -16,9 +16,10 @@ The Pico controls an Si5351A clock module to obtain the switching clock for the
The display is a standard 16x2 LCD, but with an I2C interface. The display is connected through the **i2c1** channel, as well as the bus expanders for controlling the various relays. The display is a standard 16x2 LCD, but with an I2C interface. The display is connected through the **i2c1** channel, as well as the bus expanders for controlling the various relays.
## Open issues: ## Open issues:
- [ ] implement AGC - [ ] implement proper AGC
- [ ] implement RSSI - [x] implement RSSI and S-meter
- [ ] improve FFT-based signal processing - [x] improve FFT-based signal processing
- [ ] revisit Si5351A driver
## Installing and using the SDK for Windows: ## Installing and using the SDK for Windows:
For setting up the C/C++ build environment for Windows, you can follow the procedure as described in the Raspberry [Getting Started](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) document. This document also refers to a [setup script](https://github.com/ndabas/pico-setup-windows). In case this does not work, follow the instructions below. For setting up the C/C++ build environment for Windows, you can follow the procedure as described in the Raspberry [Getting Started](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) document. This document also refers to a [setup script](https://github.com/ndabas/pico-setup-windows). In case this does not work, follow the instructions below.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

86
dsp.c
Wyświetl plik

@ -63,6 +63,47 @@ void dsp_setmode(int mode)
} }
/*
* S-Meter is based on RSSI, which is in fact the signal level in the preprocessor.
* The length of the (I,Q) vector is taken as reference for RSSI, where
* S value is highest bit set, i.e. RSSI of 512 corresponds with S-9 (S=log2(RSSI))
* This value was calibrated roughly by using the sam antenna and
* comparing an IC R71-E with my uSDR HW implementation and ADC_INT=8.
* +20dB means 10x the S-9 RSSI level, or >5120
* +40dB means 100x the S-9 RSSI level, or >51200
*/
#define S940 51200
#define S930 16180
#define S920 5120
#define S910 1618
#define S9 512
#define S8 256
#define S7 128
#define S6 64
#define S5 32
#define S4 16
#define S3 8
#define S2 4
#define S1 2
volatile uint32_t s_rssi; // 1.. >51200
int get_sval(void)
{
uint32_t s_val = s_rssi;
if (s_val>S940) return(94); // Return max 2 digits!
if (s_val>S930) return(93);
if (s_val>S920) return(92);
if (s_val>S910) return(91);
if (s_val>S9) return(9);
if (s_val>S8) return(8);
if (s_val>S7) return(7);
if (s_val>S6) return(6);
if (s_val>S5) return(5);
if (s_val>S4) return(4);
if (s_val>S3) return(3);
if (s_val>S2) return(2);
return(1);
}
/* /*
* AGC reference level is log2(0x40) = 6, where 0x40 is the MSB of half DAC_RANGE * AGC reference level is log2(0x40) = 6, where 0x40 is the MSB of half DAC_RANGE
* 1/AGC_DECAY and 1/AGC_ATTACK are multipliers before agc_gain value integrator * 1/AGC_DECAY and 1/AGC_ATTACK are multipliers before agc_gain value integrator
@ -71,6 +112,7 @@ void dsp_setmode(int mode)
* So when delta is 1, and attack is 64, the time is 64/15625 = 4msec (fast attack) * So when delta is 1, and attack is 64, the time is 64/15625 = 4msec (fast attack)
* The decay time is about 100x this value * The decay time is about 100x this value
* Slow attack would be about 4096 * Slow attack would be about 4096
*/ */
#define AGC_REF 6 #define AGC_REF 6
#define AGC_DECAY 8192 #define AGC_DECAY 8192
@ -79,6 +121,7 @@ void dsp_setmode(int mode)
#define AGC_DIS 32766 #define AGC_DIS 32766
volatile uint16_t agc_decay = AGC_DIS; volatile uint16_t agc_decay = AGC_DIS;
volatile uint16_t agc_attack = AGC_DIS; volatile uint16_t agc_attack = AGC_DIS;
void dsp_setagc(int agc) void dsp_setagc(int agc)
{ {
switch(agc) switch(agc)
@ -135,6 +178,7 @@ void dsp_setvox(int vox)
#define ABS(x) ( (x)<0 ? -(x) : (x) ) #define ABS(x) ( (x)<0 ? -(x) : (x) )
/* /*
* Calculation of vector length:
* Z = alpha*max(i,q) + beta*min(i,q); * Z = alpha*max(i,q) + beta*min(i,q);
* alpha = 1/1, beta = 3/8 (error<6.8%) * alpha = 1/1, beta = 3/8 (error<6.8%)
* alpha = 15/16, beta = 15/32 (error<6.25%) * alpha = 15/16, beta = 15/32 (error<6.25%)
@ -153,12 +197,17 @@ inline int16_t mag(int16_t i, int16_t q)
/* /*
* Note: A simple regression IIR single pole low pass filter could be made for anti-aliasing. * Note: A simple regression IIR single pole low pass filter could be made for anti-aliasing.
* y(n) = (1-a)*y(n-1) + a*x(n) = y(n-1) + a*(x(n) - y(n-1)) * y(n) = (1-a)*y(n-1) + a*x(n) = y(n-1) + a*(x(n) - y(n-1))
* in this a = T / (T + R*C) * in this a = T / (T + R*C)
* Example: * Example:
* T is sample period (e.g. 64usec) * T is sample period (e.g. 64usec)
* RC the desired RC time: T*(1-a)/a. * RC the desired RC time: T*(1-a)/a.
* example: a=1/256 : RC = 255*64usec = 16msec (65Hz) * example: a=1/256 : RC = 255*64usec = 16msec (65Hz)
* Alternative faster implementation with higher accuracy
* y(n) = y(n-1) + (x(n) - y(n-1)>>b)
* Here the filtered value is maintained in higher accuracy, i.e. left shifted by b bits.
* Before using the value: y >> b.
* Also, for RC value 1/a = 1<<b, or RC = ((1<<b)-1)*64us
*/ */
@ -168,10 +217,10 @@ volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample
/*** Include the desired DSP engine ***/ /*** Include the desired DSP engine ***/
#if DSP_FFT == 1 #if DSP_FFT == 1
#define AGC_TOP 16383L #define AGC_TOP 2047L
#include "dsp_fft.c" #include "dsp_fft.c"
#else #else
#define AGC_TOP 1500L #define AGC_TOP 2047L
#include "dsp_tim.c" #include "dsp_tim.c"
#endif #endif
@ -182,14 +231,15 @@ volatile int32_t q_sample, i_sample, a_sample; // Latest processed sample
* 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)
*/ */
#define LSH 8 // Shift for higher accuracy of level #define LSH 8 // Shift for higher accuracy of level, also LPF
#define BSH 8 // Shift for higher accuracy of bias #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_BIASS ADC_BIAS<<BSH // Shifted initial ADC bias
#define ADC_INT 8 // Nr of samples for integration (max 8) #define ADC_INT 8 // Nr of samples for integration (max 8)
volatile int16_t adc_sample[ADC_INT][3]; // ADC samples collection 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_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 int32_t adc_result[3]; // ADC filtered result for further processing
volatile uint32_t adc_level[3] = {10<<LSH,10<<LSH,10<<LSH}; // Levels for ADC channels volatile uint32_t adc_level[3] = {ADC_LEVELS, ADC_LEVELS, ADC_LEVELS}; // Levels for ADC channels
volatile int adccnt = 0; // Sampling overflow indicator volatile int adccnt = 0; // Sampling overflow indicator
@ -252,6 +302,7 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
*/ */
// Get samples and correct for DC bias // Get samples and correct for DC bias
// RC: ((1<<BSH)-1)*64usec = 16msec
adc_result[0] = 0; adc_result[0] = 0;
adc_result[1] = 0; adc_result[1] = 0;
adc_result[2] = 0; adc_result[2] = 0;
@ -277,17 +328,24 @@ bool __not_in_flash_func(dsp_callback)(repeating_timer_t *t)
adc_run(true); // Start ADC again adc_run(true); // Start ADC again
// Calculate and save level, left shifted by LSH // Calculate and save level, value is left shifted by LSH = 8
// a=1/1024 : RC = 1023*64usec = 65msec (15Hz) // RC: ((1<<LSH)-1)*64usec = 16msec
adc_level[0] = (1023*adc_level[0] + (ABS(adc_result[0])<<LSH))/1024; adc_level[0] += (ABS(adc_result[0]))-(adc_level[0]>>LSH);
adc_level[1] = (1023*adc_level[1] + (ABS(adc_result[1])<<LSH))/1024; adc_level[1] += (ABS(adc_result[1]))-(adc_level[1]>>LSH);
adc_level[2] = (1023*adc_level[2] + (ABS(adc_result[2])<<LSH))/1024; adc_level[2] += (ABS(adc_result[2]))-(adc_level[2]>>LSH);
// Crude AGC mechanism ** TO BE IMPROVED ** // Derive RSSI value from RX vector length
// Crude AGC mechanism **TO BE IMPROVED**
if (!tx_enabled) if (!tx_enabled)
{ {
temp = (MAX(adc_level[1], adc_level[0]))>>LSH; // Max I or Q uint32_t i=adc_level[1],q=adc_level[0];
rx_agc = (temp==0) ? AGC_TOP : AGC_TOP/temp; // calculate required AGC factor if (i>q)
temp = (MAX(i,((29*i/32) + (61*q/128))))>>LSH;
else
temp = (MAX(q,((29*q/32) + (61*i/128))))>>LSH;
s_rssi = MAX(1,temp);
rx_agc = AGC_TOP/s_rssi; // calculate required AGC factor
if (rx_agc==0) rx_agc=1;
} }
#if DSP_FFT == 1 #if DSP_FFT == 1

3
dsp.h
Wyświetl plik

@ -35,6 +35,9 @@
/** DSP module interface **/ /** DSP module interface **/
extern volatile uint32_t s_rssi;
int get_sval(void);
extern volatile bool tx_enabled; // Determined by (vox_active || ptt_active) extern volatile bool tx_enabled; // Determined by (vox_active || ptt_active)
#define VOX_OFF 0 #define VOX_OFF 0

Wyświetl plik

@ -155,10 +155,10 @@ bool __not_in_flash_func(rx)(void)
int16_t *ip, *qp, *ap, *xip, *xqp; int16_t *ip, *qp, *ap, *xip, *xqp;
int16_t peak; int16_t peak;
b = dsp_active; // Point to Active buffer b = dsp_active; // Point to Active sample buffer
/*** Copy saved I/Q buffers to FFT buffer ***/ /*** Copy saved I/Q buffers to FFT buffer ***/
if (++b > 2) b = 0; // Point to Old Saved 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];
for (i=0; i<BUFSIZE; i++) for (i=0; i<BUFSIZE; i++)
@ -166,7 +166,7 @@ bool __not_in_flash_func(rx)(void)
*xip++ = *ip++; *xip++ = *ip++;
*xqp++ = *qp++; *xqp++ = *qp++;
} }
if (++b > 2) b = 0; // Point to New Saved buffer if (++b > 2) b = 0; // Point to New Saved sample buffer
ip = &I_buf[b][0]; xip = &XI_buf[BUFSIZE]; ip = &I_buf[b][0]; xip = &XI_buf[BUFSIZE];
qp = &Q_buf[b][0]; xqp = &XQ_buf[BUFSIZE]; qp = &Q_buf[b][0]; xqp = &XQ_buf[BUFSIZE];
for (i=0; i<BUFSIZE; i++) for (i=0; i<BUFSIZE; i++)
@ -244,16 +244,16 @@ bool __not_in_flash_func(rx)(void)
/*** Export FFT buffer to A ***/ /*** Export FFT buffer to A ***/
b = dsp_active; // Assume active buffer not changed, i.e. no overruns b = dsp_active; // Assume active buffer not changed, i.e. no overruns
if (++b > 2) b = 0; // Point to oldest if (++b > 2) b = 0; // Point to oldest (will be next for output)
ap = &A_buf[b][0]; xip = &XI_buf[0]; ap = &A_buf[b][0]; xip = &XI_buf[BUFSIZE];
for (i=0; i<BUFSIZE; i++) for (i=0; i<BUFSIZE; i++)
{ {
*ap++ = *xip++; // Copy oldest results *ap++ = *xip++; // Copy newest results
} }
/*** Scale down into DAC_RANGE! ***/ /*** Scale down into DAC_RANGE! ***/
peak = 64; peak = 256;
for (i=0; i<BUFSIZE; i++) for (i=0; i<BUFSIZE; i++)
{ {
A_buf[b][i] /= peak; A_buf[b][i] /= peak;

Wyświetl plik

@ -139,7 +139,7 @@ bool __not_in_flash_func(rx)(void)
* Scale and clip output, * Scale and clip output,
* Send to audio DAC output * Send to audio DAC output
*/ */
a_sample = (a_sample/32) + DAC_BIAS; // -15dB and add bias level a_sample = (a_sample/64) + DAC_BIAS; // -18dB and add bias level
if (a_sample > DAC_RANGE) // Clip to DAC range if (a_sample > DAC_RANGE) // Clip to DAC range
a_sample = DAC_RANGE; a_sample = DAC_RANGE;
else if (a_sample<0) else if (a_sample<0)

Wyświetl plik

@ -42,7 +42,7 @@
#include "fix_fft.h" #include "fix_fft.h"
/** Fixed point Sine lookup table, [-1, 1] == [-32766, 32767] **/ /** Fixed point Sine lookup table, [-1, 1] == [-32768, 32767] **/
int16_t Sine[3*FFT_SIZE/4] = int16_t Sine[3*FFT_SIZE/4] =
{ {
0, 201, 402, 603, 804, 1005, 1206, 1406, 0, 201, 402, 603, 804, 1005, 1206, 1406,
@ -217,13 +217,13 @@ static int16_t bitrev[FFT_SIZE] =
/* /*
* Assume Q(0,15) notation, 1 sign, 0 int, 15 frac bits * Assume Q(0,15) notation, 1 sign, 0 int, 15 frac bits
*/ */
int16_t __not_in_flash_func(FIX_MPY)(int16_t a, int16_t b) // Fixed-point mpy and scaling int16_t __not_in_flash_func(FIX_MPY)(int16_t a, int16_t b) // Fixed-point mpy and scaling
{ {
int32_t c; int32_t c;
c = (int32_t)a * (int32_t)b; // multiply c = (int32_t)a * (int32_t)b; // multiply
c = c + 0x4000; // and round up c = c + 0x4000; // and round up
c = c >>15; // Shift right fractional bits c = c >> 15; // Shift right fractional bits
return((int16_t)c); // Return scaled product return((int16_t)c); // Return scaled product
} }
@ -287,6 +287,7 @@ int __not_in_flash_func(fix_fft)(int16_t *fr, int16_t *fi, bool inverse)
j = m << (k-1); // 0 <= j < FFT_SIZE/2 j = m << (k-1); // 0 <= j < FFT_SIZE/2
wr = Sine[j+FFT_SIZE/4]; // Real part, i.e. Cosine wr = Sine[j+FFT_SIZE/4]; // Real part, i.e. Cosine
wi = inverse ? Sine[j] : -Sine[j]; // Imaginary part wi = inverse ? Sine[j] : -Sine[j]; // Imaginary part
if (shift) { wr = wr/2; wi = wi/2; } // Scale factors by 1/2 if (shift) { wr = wr/2; wi = wi/2; } // Scale factors by 1/2
for (i=m; i<FFT_SIZE; i+=(step*2)) // #cycles: FFT_SIZE/step for (i=m; i<FFT_SIZE; i+=(step*2)) // #cycles: FFT_SIZE/step
@ -294,10 +295,8 @@ int __not_in_flash_func(fix_fft)(int16_t *fr, int16_t *fi, bool inverse)
j = i + step; // re-assign j ! j = i + step; // re-assign j !
tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]); // Complex multiply tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]); // Complex multiply
ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]); ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]);
if (shift) qr = shift ? fr[i]/2 : fr[i];
{ qr = fr[i]/2; qi = fi[i]/2; } qi = shift ? fi[i]/2 : fi[i];
else
{ qr = fr[i]; qi = fi[i]; }
fr[i] = qr + tr; fr[i] = qr + tr;
fi[i] = qi + ti; fi[i] = qi + ti;
fr[j] = qr - tr; fr[j] = qr - tr;

8
hmi.c
Wyświetl plik

@ -110,7 +110,7 @@ char hmi_noption[HMI_NSTATES] = {HMI_NTUNE, HMI_NMODE, HMI_NAGC, HMI_NPRE, HMI_N
char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC","Pre","VOX"}; // Indexed by hmi_state char hmi_o_menu[HMI_NSTATES][8] = {"Tune","Mode","AGC","Pre","VOX"}; // Indexed by hmi_state
char hmi_o_mode[HMI_NMODE][8] = {"USB","LSB","AM ","CW "}; // Indexed by hmi_sub[HMI_S_MODE] char hmi_o_mode[HMI_NMODE][8] = {"USB","LSB","AM ","CW "}; // Indexed by hmi_sub[HMI_S_MODE]
char hmi_o_agc [HMI_NAGC][8] = {"NoGC","Slow","Fast"}; // Indexed by hmi_sub[HMI_S_AGC] char hmi_o_agc [HMI_NAGC][8] = {"NoGC","Slow","Fast"}; // Indexed by hmi_sub[HMI_S_AGC]
char hmi_o_pre [HMI_NPRE][8] = {"-30dB","-20dB","-10dB","0dB","+10dB"}; // Indexed by hmi_sub[HMI_S_PRE] char hmi_o_pre [HMI_NPRE][8] = {"-30dB","-20dB","-10dB"," 0dB","+10dB"}; // Indexed by hmi_sub[HMI_S_PRE]
char hmi_o_vox [HMI_NVOX][8] = {"NoVOX","VOX-L","VOX-M","VOX-H"}; // Indexed by hmi_sub[HMI_S_VOX] char hmi_o_vox [HMI_NVOX][8] = {"NoVOX","VOX-L","VOX-M","VOX-H"}; // Indexed by hmi_sub[HMI_S_VOX]
char hmi_o_bpf [HMI_NBPF][8] = {"<2.5","2-6","5-12","10-24","20-40"}; // Indexed by char hmi_o_bpf [HMI_NBPF][8] = {"<2.5","2-6","5-12","10-24","20-40"}; // Indexed by
@ -314,7 +314,11 @@ void hmi_evaluate(void)
char s[32]; char s[32];
// Print top line of display // Print top line of display
sprintf(s, "%s %7.1f %c%3d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, (tx_enabled?0x07:0x06), (tx_enabled?0:920)); if (tx_enabled)
sprintf(s, "%s %7.1f %c %-2d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, 0x07, 0);
else
sprintf(s, "%s %7.1f %cS%-2d", hmi_o_mode[hmi_sub[HMI_S_MODE]], (double)hmi_freq/1000.0, 0x06, get_sval());
lcd_writexy(0,0,s); lcd_writexy(0,0,s);
// Print bottom line of dsiplay, depending on state // Print bottom line of dsiplay, depending on state

Wyświetl plik

@ -177,16 +177,13 @@ void mon_or(void)
/* /*
* ADC and AGC levels * ADC and AGC levels
*/ */
extern volatile uint32_t adc_level[3];
extern volatile int32_t rx_agc; extern volatile int32_t rx_agc;
extern volatile int adccnt; extern volatile int adccnt;
void mon_adc(void) void mon_adc(void)
{ {
// Print results // Print results
printf("ADC0: %5u/2048\n", adc_level[0]>>8); printf("RSSI: %5u\n", s_rssi);
printf("ADC1: %5u/2048\n", adc_level[1]>>8); printf("AGC : %5d\n", rx_agc);
printf("ADC2: %5u/2048\n", adc_level[2]>>8);
printf("AGC : %7d\n", rx_agc);
printf("ADCc: %5d\n", adccnt); printf("ADCc: %5d\n", adccnt);
} }