kopia lustrzana https://github.com/threeme3/usdx
Changed sdr_rx() with improved efficiency (and cleaner junk section). Changed CW filter into integer logic (faster).
rodzic
8c67a37c15
commit
21c81482ce
676
QCX-SSB.ino
676
QCX-SSB.ino
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// https://github.com/threeme3/QCX-SSB
|
||||
|
||||
#define VERSION "1.01h"
|
||||
#define VERSION "1.01i"
|
||||
|
||||
// QCX pin defintion
|
||||
#define LCD_D4 0
|
||||
|
@ -381,12 +381,11 @@ void si5351_powerDown()
|
|||
si5351_SendRegister(SI_CLK_OE, 0b11111111); // Disable all CLK outputs
|
||||
}
|
||||
|
||||
const char* mode_label[] = { "LSB", "USB", "CW ", "AM ", "FM " };
|
||||
enum mode_t { LSB, USB, CW, AM, FM };
|
||||
volatile uint8_t mode = USB;
|
||||
const char* mode_label[] = { "LSB", "USB", "CW ", "AM ", "FM " };
|
||||
volatile bool change = true;
|
||||
volatile int32_t freq = 7074000;
|
||||
|
||||
volatile bool ptt = false;
|
||||
volatile uint8_t tx = 0;
|
||||
volatile bool vox_enable = false;
|
||||
|
@ -394,6 +393,12 @@ enum dsp_cap_t { ANALOG, DSP, SDR };
|
|||
static uint8_t dsp_cap = 0;
|
||||
static uint8_t ssb_cap = 0;
|
||||
volatile bool att_enable = false;
|
||||
//#define PROFILING 1
|
||||
#ifdef PROFILING
|
||||
volatile uint32_t numSamples = 0;
|
||||
#else
|
||||
volatile uint8_t numSamples = 0;
|
||||
#endif
|
||||
|
||||
inline void txen(bool en)
|
||||
{
|
||||
|
@ -491,12 +496,6 @@ inline int16_t ssb(int16_t in)
|
|||
return dp * (-F_SAMP_TX / _UA);
|
||||
}
|
||||
|
||||
//#define PROFILING 1
|
||||
#ifdef PROFILING
|
||||
volatile uint32_t numSamples = 0;
|
||||
#else
|
||||
volatile uint16_t numSamples = 0;
|
||||
#endif
|
||||
#define MIC_ATTEN 0 // 0*6dB attenuation (note that the LSB bits are quite noisy)
|
||||
|
||||
// This is the ADC ISR, issued with sample-rate via timer1 compb interrupt.
|
||||
|
@ -577,36 +576,15 @@ char cw(int16_t in)
|
|||
return ch;
|
||||
}
|
||||
|
||||
inline int16_t filt_cwn(int16_t v)
|
||||
{ // 2nd Order Bandpass 650-840Hz (SR=8kHz) IIR in Direct Form I
|
||||
float zx0 = v;
|
||||
|
||||
static float za1,za2;
|
||||
static float zb1,zb2;
|
||||
zx0=(zx0+-2.0*za1+za2+21.0*zb1+-11.6*zb2)*5.0/64.0; //zx0=(5.0*zx0+-10.0*za1+5.0*za2+105.0*zb1+-58.0*zb2)/64.0;
|
||||
za2=za1;
|
||||
za1=v;
|
||||
zb2=zb1;
|
||||
zb1=zx0;
|
||||
|
||||
static float zc1,zc2;
|
||||
zx0=(zx0+2.0*zb1+zb2+97.0*zc1+-57.0*zc2)/64.0;
|
||||
zc2=zc1;
|
||||
zc1=zx0;
|
||||
|
||||
return zx0;
|
||||
}
|
||||
|
||||
static float gain = 1.0;
|
||||
|
||||
static char out[] = " ";
|
||||
volatile bool cw_event = false;
|
||||
|
||||
volatile uint8_t volume = 8;
|
||||
//#define F_SAMP_RX 156250
|
||||
//#define F_SAMP_RX 78125
|
||||
//#define F_SAMP_RX 62500 //overrun; sample rate of 55500 can be obtained
|
||||
#define F_SAMP_RX 52083
|
||||
#define F_SAMP_RX 62500 //overrun; sample rate of 55500 can be obtained
|
||||
//#define F_SAMP_RX 52083
|
||||
//#define F_SAMP_RX 44643
|
||||
//#define F_SAMP_RX 39062
|
||||
//#define F_SAMP_RX 34722
|
||||
|
@ -614,6 +592,8 @@ volatile uint8_t volume = 8;
|
|||
//#define F_SAMP_RX 28409
|
||||
#define F_ADC_CONV 192307
|
||||
|
||||
volatile uint8_t volume = 8;
|
||||
|
||||
inline int16_t agc(int16_t in)
|
||||
{ // source: Lyons Understanding Digital Signal Processing 3rd edition 13.30
|
||||
float out = in * gain;
|
||||
|
@ -626,245 +606,204 @@ inline int16_t agc(int16_t in)
|
|||
|
||||
#define JUNK 1
|
||||
#ifdef JUNK
|
||||
// This is here only for historical purpose and because of code alignment (makes sdr_rx faster)
|
||||
void dsp_rx()
|
||||
{ // jitter dependent things first
|
||||
ADCSRA |= (1 << ADSC); // start next ADC conversion (trigger ADC interrupt if ADIE flag is set)
|
||||
int16_t adc = (ADCL | (ADCH << 8)) - 512; // ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
|
||||
|
||||
static int16_t dc;
|
||||
dc += (adc - dc) / 2;
|
||||
int16_t ac = adc - dc; // DC decoupling
|
||||
//#define RX_SIMPLE 1
|
||||
#ifdef RX_SIMPLE
|
||||
ac = agc(ac);
|
||||
if(mode == CW) ac = filt_cwn(ac /* *64 */ * 16 );
|
||||
OCR1AL = ac + 128;
|
||||
#endif
|
||||
|
||||
#define RX_CIC 1
|
||||
#ifdef RX_CIC
|
||||
// Decimating 2nd Order CIC filter
|
||||
#define R 8 // Rate change from 62.5kSPS to 7812.5SPS
|
||||
static int16_t zi1, zi2, zd1, zd2;
|
||||
zi2 = zi1 + zi2; // Integrator section
|
||||
zi1 = ac + zi1;
|
||||
if((numSamples % R) == 0){
|
||||
int16_t d1 = zi2 - zd1; // Comb section
|
||||
ac = d1 - zd2;
|
||||
zd2 = d1;
|
||||
zd1 = zi2;
|
||||
|
||||
if(mode == CW) filt_cwn(ac >> (16-volume));//filt_cwn(ac * volume/256 ); //ac = filt_cwn(ac /* *64 */ * 4 );
|
||||
else ac = (volume) ? ac >> (16-volume) : 0;//ac * volume/256;//ac >> drive; //ac >> 3; //ac = agc(ac);
|
||||
|
||||
ac=min(max(ac, -128), 127); // clip
|
||||
OCR1AL = ac + 128;
|
||||
}
|
||||
#endif
|
||||
//#define CW_DECODER 1
|
||||
#ifdef CW_DECODER
|
||||
char ch = cw(adc >> 0);
|
||||
if(ch){
|
||||
for(int i=0; i!=15;i++) out[i]=out[i+1];
|
||||
out[15] = ch;
|
||||
cw_event = true;
|
||||
}
|
||||
#endif
|
||||
numSamples++;
|
||||
// Having this function included here and referenced makes sdr_rx faster 15% faster (normally including filt_cwn() in sdr_rx() makes the thing slower for an unknown reason)
|
||||
void junk()
|
||||
{
|
||||
filt_cwn(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline int16_t filt_cwn(int16_t v)
|
||||
{ // 2nd Order Bandpass 650-840Hz (SR=8kHz) IIR in Direct Form I
|
||||
|
||||
int16_t zx0 = v;
|
||||
|
||||
static int16_t za1,za2;
|
||||
static int16_t zb1,zb2;
|
||||
zx0=(5L*(zx0-2*za1+za2)+105L*zb1-58L*zb2)/64L; //zx0=(zx0+-2*za1+za2+21*zb1+-12*zb2); zx0=zx0/16+zx0/64; //zx0=(5.0*zx0+-10.0*za1+5.0*za2+105.0*zb1+-58.0*zb2)/64.0;
|
||||
za2=za1;
|
||||
za1=v;
|
||||
zb2=zb1;
|
||||
zb1=zx0;
|
||||
|
||||
static int16_t zc1,zc2;
|
||||
zx0=((int32_t)(zx0+2*zb1+zb2)+97L*zc1-57L*zc2)/64L; //zx0=(zx0+2*zb1+zb2)/64 + zc1*3/2 - zc2*9/10; //zx0=(zx0+2.0*zb1+zb2+97.0*zc1+-57.0*zc2)/64.0;
|
||||
zc2=zc1;
|
||||
zc1=zx0;
|
||||
|
||||
return zx0;
|
||||
}
|
||||
|
||||
/*
|
||||
iterate through modes always from begin, skipping the current one
|
||||
code definitions and re-use for comb, integrator, dc decoupling, arctan
|
||||
add sdr_rx capability to skip ADCMUX changing and Q processing, (while oversampling I branch?)
|
||||
in func_ptr for different mode types
|
||||
agc based on rms128
|
||||
agc based on rms256
|
||||
skip adc processing while smeter lcd update?
|
||||
vox by sampling mic port in sdr_rx?
|
||||
|
||||
*/
|
||||
|
||||
volatile uint8_t admuxi, admuxq;
|
||||
volatile uint32_t rms128 = 0;
|
||||
//#define ADC_NR 1 // Stop CPU at ADC conversion, reduces noise but costs 30% CPU performance when ADC prescaler is 8
|
||||
typedef void (*func_t)(void);
|
||||
#ifdef JUNK
|
||||
volatile func_t func_ptr = junk;
|
||||
#else
|
||||
volatile func_t func_ptr;
|
||||
#endif
|
||||
|
||||
// sdr_rx() is sampling the ADC0 (I) and ADC1 (Q) inputs in alternating fashion, and generating a PWM output at OC1A. For both
|
||||
// I and Q samples, a DC-decoupling and CIC down-sampling is performed by integrating the samples and for each R samples performing
|
||||
// a comb and post-processing step where the I and Q results are demodulated into a single signal which is CIC upsampled again via
|
||||
// a comb; the upsampled signal is integrated and ouput via PWM.
|
||||
//
|
||||
// The ADC sampling is governed by Timer2 compare match A interrupt. When the rate is too high for the CPU, sdr_rx() is still performing
|
||||
// well: the integration of each sample takes longer than the sample-period, the next interrupt is scheduled later (once the current interrupt
|
||||
// finishes) or is missed due to a delay that has been build-up and wwhere an interrupt happens while an interrupt flag was already
|
||||
// set. As a result, the actual sample rate is a fraction of the timer rate, and the CPU is freed for the moments an interrupt
|
||||
// is missed. Since the time for sampling and integrating is roughly the same processing time for both the I and Q channel,
|
||||
// a constant sample-rate is obtained when next interrupts are delayed and scheduled. Missing samples however disturbs the
|
||||
// continuous sampling, and therefore cause a (random) phase shift errors for on the next series of samples. But, since these phase-shift
|
||||
// are instantly and and effective on both the I/Q inputs and PWM output, the resulting output do contain a constant average of
|
||||
// these phase-shift errors, and hence are not harmful. Hence, predicted sample rate = F_SAMP_RX / CPU_load_rx_avg
|
||||
// [1] ATMEGA328P datasheet, chapter 6.7, "Reset and Interrupt Handling". Current performance figures:
|
||||
// CPU load figures for numSamples[4..12]={250, 125, 125, 125, 125, 125, 125, 225} at R=4, Fs=62.5k, DUC on
|
||||
// CPU load figures for numSamples[4..12]={187, 104, 104, 104, 104, 104, 104, 187} at R=4, Fs=52k, DUC on
|
||||
volatile uint32_t absavg256 = 0;
|
||||
volatile uint8_t admux[2];
|
||||
volatile bool _init;
|
||||
volatile int16_t ocomb, i, q, qh;
|
||||
#undef R // Decimating 2nd Order CIC filter
|
||||
#define R 4 // Rate change from 52.083/2 kSPS to 6510.4SPS, providing 12dB gain
|
||||
|
||||
void sdr_rx()
|
||||
{
|
||||
static int16_t ozi1, ozi2, ozd1, ozd2, ocomb;
|
||||
static int16_t i, q, qh;
|
||||
#undef R // Decimating 2nd Order CIC filter
|
||||
//#define R 8 // Rate change from 62.5/2 kSPS to 3906.25SPS, providing 18dB gain
|
||||
//#define R 4 // Rate change from 62.5/2 kSPS to 7812.5SPS, providing 12dB gain
|
||||
#define R 4 // Rate change from 52.083/2 kSPS to 6510.4SPS, providing 12dB gain
|
||||
static int16_t dc, zi1, zi2, zd1, zd2;
|
||||
|
||||
// process I for even samples
|
||||
if((numSamples % 2) == 0){ // [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
|
||||
ADMUX = admuxq; //1 | (1 << REFS1) | (1 << REFS0); // prepare next Q conversion, 1v1 ref enabled
|
||||
#ifdef ADC_NR
|
||||
interrupts();
|
||||
sleep_cpu();
|
||||
// noInterrupts();
|
||||
#else
|
||||
ADCSRA |= (1 << ADSC); // start next ADC conversion (trigger ADC interrupt if ADIE flag is set)
|
||||
#endif
|
||||
int16_t adc = (ADCL | (ADCH << 8)) - 512; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
|
||||
// process I for even samples [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
|
||||
ADMUX = admux[1]; // set MUX for next conversion
|
||||
ADCSRA |= (1 << ADSC); // start next ADC conversion
|
||||
func_ptr = sdr_rx_2; // processing function for next conversion
|
||||
int16_t adc = (ADCL | (ADCH << 8)) - 512; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
|
||||
sdr_rx_common();
|
||||
|
||||
// Correct I/Q sample delay by means of linear interpolation
|
||||
static int16_t prev_adc;
|
||||
int16_t corr_adc = (prev_adc + adc) / 2;
|
||||
prev_adc = adc;
|
||||
// Only for I: correct I/Q sample delay by means of linear interpolation
|
||||
static int16_t prev_adc;
|
||||
int16_t corr_adc = (prev_adc + adc) / 2;
|
||||
prev_adc = adc;
|
||||
adc = corr_adc;
|
||||
|
||||
static int16_t dc;
|
||||
dc += (corr_adc - dc) / 2;
|
||||
int16_t ac = corr_adc - dc; // DC decoupling
|
||||
dc += (adc - dc) / 2;
|
||||
int16_t ac = adc - dc; // DC decoupling
|
||||
zi2 = zi1 + zi2; // Integrator section
|
||||
zi1 = ac + zi1;
|
||||
|
||||
static int16_t zi1, zi2, zd1, zd2;
|
||||
zi2 = zi1 + zi2; // Integrator section
|
||||
zi1 = ac + zi1;
|
||||
if((numSamples % (R*2)) == 0){ // I-Comb branch [175% CPU@R=4;Fs=62.5k]
|
||||
int16_t d1 = zi2 - zd1; // Comb section
|
||||
static int16_t v[9];
|
||||
v[8] = d1 - zd2;
|
||||
zd2 = d1;
|
||||
zd1 = zi2;
|
||||
//if((numSamples % (R*2)) == 0){
|
||||
if(numSamples == 0){
|
||||
int16_t d1 = zi2 - zd1; // Comb section
|
||||
int16_t d2 = d1 - zd2;
|
||||
zd2 = d1;
|
||||
zd1 = zi2;
|
||||
|
||||
for(uint8_t j = 0; j != 8; j++) v[j] = v[j + 1]; // (25%CPU)
|
||||
i = v[0]; // Delay to match Hilbert transform on Q branch
|
||||
// post processing I and Q results
|
||||
static int16_t v[9];
|
||||
v[8] = d2;
|
||||
|
||||
//for(uint8_t j = 0; j != 8; j++) v[j] = v[j + 1]; // (25%CPU)
|
||||
v[0] = v[1]; v[1] = v[2]; v[2] = v[3]; v[3] = v[4]; v[4] = v[5]; v[5] = v[6]; v[6] = v[7]; v[7] = v[8];
|
||||
i = v[0]; // Delay to match Hilbert transform on Q branch
|
||||
|
||||
//i = i>>(drive-4);
|
||||
//q = q>>(drive-4);
|
||||
int16_t ac = i + qh;
|
||||
static uint8_t absavg256cnt;
|
||||
if(!(absavg256cnt--)){ absavg256 = 0; } else absavg256 += abs(ac);
|
||||
|
||||
// post processing I and Q results
|
||||
ac = i + qh;
|
||||
if((numSamples % (R*2*128)) == 0) rms128 = 0; else rms128 += abs(ac);
|
||||
|
||||
if(mode == AM) { // (12%CPU for the mode selection etc)
|
||||
{ static int16_t dc;
|
||||
dc += (i - dc) / 2;
|
||||
i = i - dc; } // DC decoupling
|
||||
{ static int16_t dc;
|
||||
dc += (q - dc) / 2;
|
||||
q = q - dc; } // DC decoupling
|
||||
ac = magn(i, q); //(25%CPU)
|
||||
{ static int16_t dc;
|
||||
dc += (ac - dc) / 2;
|
||||
ac = ac - dc; } // DC decoupling
|
||||
} else if(mode == FM){
|
||||
static int16_t z1;
|
||||
int16_t z0 = arctan3(q, i);
|
||||
ac = z0 - z1;
|
||||
z1 = z0;
|
||||
} // needs: p.12 https://www.veron.nl/wp-content/uploads/2014/01/FmDemodulator.pdf
|
||||
else { ; } // USB, LSB, CW
|
||||
if(1)//(mode != LSB && mode != USB)
|
||||
ac >>= (16-volume);
|
||||
else
|
||||
ac = agc(ac);
|
||||
if(mode == CW){
|
||||
ac = filt_cwn(ac << 4);
|
||||
if(mode == AM) { // (12%CPU for the mode selection etc)
|
||||
{ static int16_t dc;
|
||||
dc += (i - dc) / 2;
|
||||
i = i - dc; } // DC decoupling
|
||||
{ static int16_t dc;
|
||||
dc += (q - dc) / 2;
|
||||
q = q - dc; } // DC decoupling
|
||||
ac = magn(i, q); //(25%CPU)
|
||||
{ static int16_t dc;
|
||||
dc += (ac - dc) / 2;
|
||||
ac = ac - dc; } // DC decoupling
|
||||
} else if(mode == FM){
|
||||
static int16_t z1;
|
||||
int16_t z0 = arctan3(q, i);
|
||||
ac = z0 - z1; // Differentiator
|
||||
z1 = z0;
|
||||
//ac = ac * (F_SAMP_RX/R) / _UA; // =ac*3.5 -> skip
|
||||
} // needs: p.12 https://www.veron.nl/wp-content/uploads/2014/01/FmDemodulator.pdf
|
||||
else { ; } // USB, LSB, CW
|
||||
if(1)//(mode != LSB && mode != USB)
|
||||
ac >>= (16-volume);
|
||||
else
|
||||
ac = agc(ac);
|
||||
if(mode == CW){
|
||||
ac = filt_cwn(ac << 6);
|
||||
//#define CW_DECODER 1
|
||||
#ifdef CW_DECODER
|
||||
char ch = cw(ac >> 0);
|
||||
if(ch){
|
||||
for(int i=0; i!=15;i++) out[i]=out[i+1];
|
||||
out[15] = ch;
|
||||
cw_event = true;
|
||||
}
|
||||
#endif
|
||||
char ch = cw(ac >> 0);
|
||||
if(ch){
|
||||
for(int i=0; i!=15;i++) out[i]=out[i+1];
|
||||
out[15] = ch;
|
||||
cw_event = true;
|
||||
}
|
||||
//static int16_t dc;
|
||||
//dc += (ac - dc) / 2;
|
||||
//ac = ac - dc; // DC decoupling
|
||||
|
||||
#define DUC 1
|
||||
#ifdef DUC
|
||||
// Output stage
|
||||
if(numSamples == 0){ ozd1= 0; ozd2 = 0; ozi1 = 0; ozi2 = 0; } // hack: on first sample init accumlators of further stages (to prevent instability)
|
||||
int16_t od1 = ac - ozd1; // Comb section
|
||||
ocomb = od1 - ozd2;
|
||||
ozd2 = od1;
|
||||
ozd1 = ac;
|
||||
#else
|
||||
#ifndef PROFILING
|
||||
OCR1AL = min(max(ac + ICR1L/2, 0), ICR1L); // center and clip wrt PWM working range
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// process Q for odd samples
|
||||
else { // [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
|
||||
ADMUX = admuxi; //0 | (1 << REFS1) | (1 << REFS0); // prepare next I conversion, 1v1 ref enabled
|
||||
#ifdef ADC_NR
|
||||
interrupts();
|
||||
sleep_cpu();
|
||||
#else
|
||||
ADCSRA |= (1 << ADSC); // start next ADC conversion (trigger ADC interrupt if ADIE flag is set)
|
||||
#endif
|
||||
int16_t adc = (ADCL | (ADCH << 8)) - 512; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
|
||||
//static int16_t dc;
|
||||
//dc += (ac - dc) / 2;
|
||||
//ac = ac - dc; // DC decoupling
|
||||
|
||||
static int16_t dc;
|
||||
dc += (adc - dc) / 2;
|
||||
int16_t ac = adc - dc; // DC decoupling
|
||||
|
||||
static int16_t zi1, zi2, zd1, zd2;
|
||||
zi2 = zi1 + zi2; // Integrator section
|
||||
zi1 = ac + zi1;
|
||||
if((numSamples % (R*2)) == ((R*2)-1) ){ // Q-Comb branch: [125% CPU@R=4;Fs=62.5k] - executed just 1 sample before executing I-Comb (and final) branch
|
||||
//interrupts();
|
||||
int16_t d1 = zi2 - zd1; // Comb section
|
||||
static int16_t v[16];
|
||||
v[15] = d1 - zd2;
|
||||
zd2 = d1;
|
||||
zd1 = zi2;
|
||||
|
||||
for(uint8_t j = 0; j != 15; j++) v[j] = v[j + 1];
|
||||
q = v[7];
|
||||
qh = ((v[0] - v[14]) * 2 + (v[2] - v[12]) * 8 + (v[4] - v[10]) * 21 + (v[6] - v[8]) * 15) / 128 + (v[6] - v[8]) / 2; // Hilbert transform, 40dB side-band rejection in 400..1900Hz (@4kSPS) when used in image-rejection scenario; (Hilbert transform require 5 additional bits)
|
||||
}
|
||||
// Output stage
|
||||
static int16_t ozd1, ozd2;
|
||||
if(_init){ ozd1= 0; ozd2 = 0; _init = false; } // hack: on first sample init accumlators of further stages (to prevent instability)
|
||||
int16_t od1 = ac - ozd1; // Comb section
|
||||
ocomb = od1 - ozd2;
|
||||
ozd2 = od1;
|
||||
ozd1 = ac;
|
||||
}
|
||||
|
||||
#ifdef DUC
|
||||
numSamples++;
|
||||
}
|
||||
|
||||
void sdr_rx_2()
|
||||
{
|
||||
static int16_t dc, zi1, zi2, zd1, zd2;
|
||||
|
||||
// process Q for odd samples [75% CPU@R=4;Fs=62.5k] (excluding the Comb branch and output stage)
|
||||
ADMUX = admux[0]; // set MUX for next conversion
|
||||
ADCSRA |= (1 << ADSC); // start next ADC conversion
|
||||
func_ptr = sdr_rx; // processing function for next conversion
|
||||
int16_t adc = (ADCL | (ADCH << 8)) - 512; // current ADC sample 10-bits analog input, NOTE: first ADCL, then ADCH
|
||||
|
||||
dc += (adc - dc) / 2;
|
||||
int16_t ac = adc - dc; // DC decoupling
|
||||
zi2 = zi1 + zi2; // Integrator section
|
||||
zi1 = ac + zi1;
|
||||
|
||||
//if((numSamples % (R*2)) == ((R*2)-1) ){
|
||||
if(numSamples == (R*2)-1){
|
||||
int16_t d1 = zi2 - zd1; // Comb section
|
||||
int16_t d2 = d1 - zd2;
|
||||
zd2 = d1;
|
||||
zd1 = zi2;
|
||||
|
||||
// Process Q samples
|
||||
static int16_t v[16];
|
||||
v[15] = d2;
|
||||
|
||||
for(uint8_t j = 0; j != 15; j++) v[j] = v[j + 1];
|
||||
q = v[7];
|
||||
qh = ((v[0] - v[14]) * 2 + (v[2] - v[12]) * 8 + (v[4] - v[10]) * 21 + (v[6] - v[8]) * 15) / 128 + (v[6] - v[8]) / 2; // Hilbert transform, 40dB side-band rejection in 400..1900Hz (@4kSPS) when used in image-rejection scenario; (Hilbert transform require 5 additional bits)
|
||||
#ifndef PROFILING
|
||||
numSamples = 0;
|
||||
} else
|
||||
numSamples++;
|
||||
#else
|
||||
}
|
||||
numSamples++;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void sdr_rx_common()
|
||||
{
|
||||
static int16_t ozi1, ozi2;
|
||||
if(_init){ ozi1 = 0; ozi2 = 0; } // hack
|
||||
// Output stage [25% CPU@R=4;Fs=62.5k]
|
||||
ozi2 = ozi1 + ozi2; // Integrator section
|
||||
ozi1 = ocomb + ozi1;
|
||||
#ifndef PROFILING
|
||||
if(volume) OCR1AL = min(max((ozi2>>5) + ICR1L/2, 0), ICR1L); // center and clip wrt PWM working range
|
||||
numSamples++;
|
||||
#endif
|
||||
#endif
|
||||
//
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
typedef void (*func_t)(void);
|
||||
static volatile func_t func_ptr = dsp_rx; //referencing dsp_rx means it is used, and because it is there sdr_rx runs faster!
|
||||
|
||||
ISR(TIMER2_COMPA_vect) // Timer2 COMPA interrupt
|
||||
{
|
||||
func_ptr();
|
||||
#ifdef PROFILING
|
||||
numSamples++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void adc_start(uint8_t adcpin, bool ref1v1, uint32_t fs)
|
||||
|
@ -1039,30 +978,11 @@ void customDelay(uint32_t _micros) //_micros=100000 is 132052us delay
|
|||
uint32_t i; for(i = 0; i != _micros * 3; i++) wdt_reset();
|
||||
}
|
||||
|
||||
uint32_t sample_amp(uint8_t pin, uint16_t osr)
|
||||
float smeter(float ref = 5.1) //= 10*log(F_SAMP_RX/R/2400) ref to 2.4kHz BW.
|
||||
{
|
||||
uint16_t avg = 1024 / 2;
|
||||
uint32_t rms = 0;
|
||||
uint16_t i;
|
||||
for(i = 0; i != 16; i++){
|
||||
uint16_t adc = analogRead(pin);
|
||||
avg = (avg + adc) / 2;
|
||||
}
|
||||
for(i = 0; i != osr; i++){ // 128 overampling is 42dB gain => with 10-bit ADC resulting in a total of 102dB DR
|
||||
uint16_t adc = analogRead(pin);
|
||||
avg = (avg + adc) / 2; // average
|
||||
rms += ((adc > avg) ? 1 : -1) * (adc - avg); // rectify based
|
||||
wdt_reset();
|
||||
}
|
||||
return rms;
|
||||
}
|
||||
|
||||
float smeter(float ref = 0.0)
|
||||
{
|
||||
float rms;
|
||||
if(dsp_cap == ANALOG) rms = ((float)sample_amp(AUDIO1, 128)) * 5.0 / (1024.0 * 128.0 * 100.0 * 120.0 / 1.750); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
if(dsp_cap == DSP) rms = (float)rms128 * 5.0 / (1024.0 * 128.0 * 100.0 * 120.0 / 1.750); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
if(dsp_cap == SDR) rms = (float)rms128 * 1.1 / (1024.0 * (float)R * 128.0 * 100.0 * 50.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
float rms = absavg256 / 256; //sqrt(256.0);
|
||||
if(dsp_cap == SDR) rms = (float)rms * 1.1 / (1024.0 * (float)R * 100.0 * 50.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
else rms = (float)rms * 5.0 / (1024.0 * (float)R * 100.0 * 120.0 / 1.750);
|
||||
float dbm = (10.0 * log10((rms * rms) / 50.0) + 30.0) - ref; //from rmsV to dBm at 50R
|
||||
static float dbm_max;
|
||||
dbm_max = max(dbm_max, dbm);
|
||||
|
@ -1081,6 +1001,24 @@ float smeter(float ref = 0.0)
|
|||
return dbm;
|
||||
}
|
||||
|
||||
uint32_t sample_amp(uint8_t pin, uint16_t osr)
|
||||
{
|
||||
uint16_t avg = 1024 / 2;
|
||||
uint32_t rms = 0;
|
||||
uint16_t i;
|
||||
for(i = 0; i != 16; i++){
|
||||
uint16_t adc = analogRead(pin);
|
||||
avg = (avg + adc) / 2;
|
||||
}
|
||||
for(i = 0; i != osr; i++){ // 128 overampling is 42dB gain => with 10-bit ADC resulting in a total of 102dB DR
|
||||
uint16_t adc = analogRead(pin);
|
||||
avg = (avg + adc) / 2; // average
|
||||
rms += ((adc > avg) ? 1 : -1) * (adc - avg); // rectify based
|
||||
wdt_reset();
|
||||
}
|
||||
return rms;
|
||||
}
|
||||
|
||||
float dbmeter(float ref = 0.0)
|
||||
{
|
||||
float rms = ((float)sample_amp(AUDIO1, 128)) * 5.0 / (1024.0 * 128.0 * 100.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
|
@ -1090,6 +1028,75 @@ float dbmeter(float ref = 0.0)
|
|||
return dbm;
|
||||
}
|
||||
|
||||
// RX I/Q calibration procedure: terminate with 50 ohm, enable CW filter, adjust R27, R24, R17 subsequently to its minimum side-band rejection value in dB
|
||||
void calibrate_iq()
|
||||
{
|
||||
lcd.setCursor(9, 0); lcd.print(blanks);
|
||||
digitalWrite(SIG_OUT, true); // loopback on
|
||||
si5351_prev_pll_freq = 0; //enforce PLL reset
|
||||
si5351_freq(freq, 0, 90); // RX in USB
|
||||
float dbc;
|
||||
si5351_alt_clk2(freq + 700);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 700);
|
||||
lcd.setCursor(0, 1); lcd.print("I-Q bal. (700 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
si5351_alt_clk2(freq + 600);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 600);
|
||||
lcd.setCursor(0, 1); lcd.print("Phase Lo (600 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
si5351_alt_clk2(freq + 800);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 800);
|
||||
lcd.setCursor(0, 1); lcd.print("Phase Hi (800 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
/*
|
||||
for(uint32_t offset = 0; offset < 3000; offset += 100){
|
||||
si5351_alt_clk2(freq + offset);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - offset);
|
||||
lcd.setCursor(0, 1); lcd.print(offset); lcd.print(" Hz"); lcd.print(blanks);
|
||||
wdt_reset(); dbmeter(dbc); delay(500); wdt_reset();
|
||||
}
|
||||
*/
|
||||
lcd.setCursor(9, 0); lcd.print(blanks); // cleanup dbmeter
|
||||
digitalWrite(SIG_OUT, false); // loopback off
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1
|
||||
change = true; //restore original frequency setting
|
||||
|
||||
}
|
||||
|
||||
void powermeter()
|
||||
{
|
||||
lcd.setCursor(9, 0); lcd.print(blanks);
|
||||
si5351_prev_pll_freq = 0; //enforce PLL reset
|
||||
si5351_freq(freq, 0, 90); // RX in USB
|
||||
si5351_alt_clk2(freq + 1000); // si5351_freq_clk2(freq + 1000);
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111000); // CLK2_EN=1, CLK1_EN,CLK0_EN=1
|
||||
digitalWrite(KEY_OUT, HIGH); //OCR1BL = 0xFF;
|
||||
digitalWrite(RX, LOW); // TX
|
||||
|
||||
//if(dsp_cap != SDR){ adc_start(1, false, F_ADC_CONV); admux[0] = ADMUX; admux[1] = ADMUX; }
|
||||
wdt_reset(); delay(100);
|
||||
|
||||
float rms;
|
||||
rms = ((float)sample_amp(AUDIO2, 128)) * 5.0 / (1024.0 * 128.0 * 100.0 / 10000.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * rx/tx switch attenuation]
|
||||
//if(dsp_cap == SDR) rms = (float)rms256 * 1.1 / (1024.0 * (float)R * 256.0 * 100.0 * 50.0 / 10000); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
|
||||
//else rms = (float)rms256 * 5.0 / (1024.0 * (float)R * 256.0 * 100.0 / 10000);
|
||||
float w = (rms * rms) / 50.0;
|
||||
float dbm = 10.0 * log10(w) + 30.0; //from rmsV to dBm at 50R
|
||||
|
||||
lcd.setCursor(9, 0); lcd.print(" "); lcd.print(w); lcd.print("W ");
|
||||
//lcd.setCursor(9, 0); lcd.print(" +"); lcd.print((int16_t)dbm ); lcd.print("dBm ");
|
||||
|
||||
digitalWrite(KEY_OUT, LOW); //OCR1BL = 0x00;
|
||||
digitalWrite(RX, HIGH); // RX
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1
|
||||
change = true; //restore original frequency setting
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void test_tx_amp()
|
||||
{
|
||||
lcd.setCursor(9, 0); lcd.print(blanks);
|
||||
|
@ -1170,88 +1177,25 @@ void calibrate_predistortion()
|
|||
change = true; //restore original frequency setting
|
||||
}
|
||||
|
||||
// RX I/Q calibration procedure: terminate with 50 ohm, enable CW filter, adjust R27, R24, R17 subsequently to its minimum side-band rejection value in dB
|
||||
void calibrate_iq()
|
||||
{
|
||||
lcd.setCursor(9, 0); lcd.print(blanks);
|
||||
digitalWrite(SIG_OUT, true); // loopback on
|
||||
si5351_prev_pll_freq = 0; //enforce PLL reset
|
||||
si5351_freq(freq, 0, 90); // RX in USB
|
||||
float dbc;
|
||||
si5351_alt_clk2(freq + 700);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 700);
|
||||
lcd.setCursor(0, 1); lcd.print("I-Q bal. (700 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
si5351_alt_clk2(freq + 600);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 600);
|
||||
lcd.setCursor(0, 1); lcd.print("Phase Lo (600 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
si5351_alt_clk2(freq + 800);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - 800);
|
||||
lcd.setCursor(0, 1); lcd.print("Phase Hi (800 Hz)"); lcd.print(blanks);
|
||||
for(; !digitalRead(BUTTONS);){ wdt_reset(); dbmeter(dbc); } for(; digitalRead(BUTTONS);) wdt_reset();
|
||||
/*
|
||||
for(uint32_t offset = 0; offset < 3000; offset += 100){
|
||||
si5351_alt_clk2(freq + offset);
|
||||
dbc = dbmeter();
|
||||
si5351_alt_clk2(freq - offset);
|
||||
lcd.setCursor(0, 1); lcd.print(offset); lcd.print(" Hz"); lcd.print(blanks);
|
||||
wdt_reset(); dbmeter(dbc); delay(500); wdt_reset();
|
||||
}
|
||||
*/
|
||||
lcd.setCursor(9, 0); lcd.print(blanks); // cleanup dbmeter
|
||||
digitalWrite(SIG_OUT, false); // loopback off
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1
|
||||
change = true; //restore original frequency setting
|
||||
|
||||
}
|
||||
|
||||
void powermeter()
|
||||
{
|
||||
lcd.setCursor(9, 0); lcd.print(blanks);
|
||||
si5351_prev_pll_freq = 0; //enforce PLL reset
|
||||
si5351_freq(freq, 0, 90); // RX in USB
|
||||
si5351_alt_clk2(freq + 1000); // si5351_freq_clk2(freq + 1000);
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111000); // CLK2_EN=1, CLK1_EN,CLK0_EN=1
|
||||
digitalWrite(KEY_OUT, HIGH); //OCR1BL = 0xFF;
|
||||
digitalWrite(RX, LOW); // TX
|
||||
delay(100);
|
||||
|
||||
float rms = ((float)sample_amp(AUDIO2, 128)) * 5.0 / (1024.0 * 128.0 * 100.0 * 50.0 / 100000.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * rx/tx switch attenuation]
|
||||
float dbm = 10.0 * log10((rms * rms) / 50.0) + 30.0; //from rmsV to dBm at 50R
|
||||
|
||||
lcd.setCursor(9, 0); lcd.print(" +"); lcd.print((int16_t)dbm); lcd.print("dBm ");
|
||||
|
||||
digitalWrite(KEY_OUT, LOW); //OCR1BL = 0x00;
|
||||
digitalWrite(RX, HIGH); // RX
|
||||
si5351_SendRegister(SI_CLK_OE, 0b11111100); // CLK2_EN=0, CLK1_EN,CLK0_EN=1
|
||||
change = true; //restore original frequency setting
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void start_rx()
|
||||
{
|
||||
//if(!vox_enable) txen(false);
|
||||
timer2_stop();
|
||||
timer1_stop();
|
||||
adc_stop();
|
||||
if(dsp_cap){
|
||||
tx = 1;
|
||||
func_ptr = sdr_rx; //enable RX DSP/SDR
|
||||
numSamples = 0;
|
||||
if(dsp_cap == SDR){
|
||||
adc_start(0, true, F_ADC_CONV); admuxi = ADMUX;
|
||||
adc_start(1, true, F_ADC_CONV); admuxq = ADMUX;
|
||||
timer2_start(F_SAMP_RX);
|
||||
timer1_start(F_SAMP_RX);
|
||||
} else { // ANALOG, DSP
|
||||
adc_start(0, false, F_ADC_CONV); admuxi = ADMUX; admuxq = ADMUX;
|
||||
timer2_start(F_SAMP_RX);
|
||||
timer1_start(F_SAMP_RX);
|
||||
}
|
||||
tx = 1; // transit from TX to RX
|
||||
_init = true;
|
||||
func_ptr = sdr_rx; //enable RX DSP/SDR
|
||||
numSamples = 0;
|
||||
if(dsp_cap == SDR){
|
||||
adc_start(0, true, F_ADC_CONV); admux[0] = ADMUX;
|
||||
adc_start(1, true, F_ADC_CONV); admux[1] = ADMUX;
|
||||
timer2_start(F_SAMP_RX);
|
||||
timer1_start(F_SAMP_RX);
|
||||
} else { // ANALOG, DSP
|
||||
adc_start(0, false, F_ADC_CONV); admux[0] = ADMUX; admux[1] = ADMUX;
|
||||
timer2_start(F_SAMP_RX);
|
||||
timer1_start(F_SAMP_RX);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1260,7 +1204,8 @@ void start_tx()
|
|||
timer2_stop();
|
||||
timer1_stop();
|
||||
adc_stop(); //disable RX DSP
|
||||
tx = 0;
|
||||
tx = 0; // transit from RX to TX
|
||||
_init = true;
|
||||
func_ptr = dsp_tx;
|
||||
numSamples = 0;
|
||||
amp = 0; // initialize
|
||||
|
@ -1286,9 +1231,9 @@ int analogSafeRead(uint8_t pin)
|
|||
return val;
|
||||
}
|
||||
|
||||
static uint8_t bandval = 4; //2
|
||||
#define N_BANDS 14 //7
|
||||
uint32_t band[] = { 472000, 1840000, 3573000, 5357000, 7074000, 10136000, 14074000, 18100000, 21074000, 24915000, 28074000, 50313000, 70101000, 144125000 }; // { 3573000, 5357000, 7074000, 10136000, 14074000, 18100000, 21074000 };
|
||||
static uint8_t bandval = 2;
|
||||
#define N_BANDS 11
|
||||
uint32_t band[] = { /*472000, 1840000,*/ 3573000, 5357000, 7074000, 10136000, 14074000, 18100000, 21074000, 24915000, 28074000, 50313000, 70101000/*, 144125000*/ }; // { 3573000, 5357000, 7074000, 10136000, 14074000, 18100000, 21074000 };
|
||||
enum step_t { STEP_10M, STEP_1M, STEP_500k, STEP_100k, STEP_10k, STEP_1k, STEP_500, STEP_100, STEP_10, STEP_1 };
|
||||
int32_t stepsizes[] = { 10000000, 1000000, 500000, 100000, 10000, 1000, 500, 100, 10, 1 };
|
||||
volatile int8_t stepsize = STEP_1k;
|
||||
|
@ -1362,35 +1307,6 @@ void powerDown()
|
|||
resetFunc();
|
||||
}
|
||||
|
||||
/*
|
||||
volatile boolean WDTalarm = false;
|
||||
ISR(WDT_vect)
|
||||
{
|
||||
wdt_disable(); // disable watchdog so the system does not restart
|
||||
WDTalarm = true; // flag the event
|
||||
}
|
||||
float getTemp()
|
||||
{
|
||||
WDTalarm = false;
|
||||
// Set the Watchdog timer from: https://www.gammon.com.au/power
|
||||
byte interval = 0b000110; // 1s=0b000110, 2s=0b000111, 4s=0b100000, 8s=0b10000
|
||||
//64ms= 0b000010, 128ms = 0b000011, 256ms= 0b000100, 512ms= 0b000101
|
||||
noInterrupts ();
|
||||
MCUSR = 0;
|
||||
WDTCSR |= 0b00011000; // set WDCE, WDE
|
||||
WDTCSR = 0b01000000 | interval; // set WDIE & delay interval
|
||||
wdt_reset(); // pat the dog
|
||||
interrupts ();
|
||||
unsigned long startTime = micros();
|
||||
while(!WDTalarm) { //sleep while waiting for the WDT
|
||||
set_sleep_mode (SLEEP_MODE_IDLE);
|
||||
noInterrupts (); sleep_enable(); interrupts (); sleep_cpu ();
|
||||
sleep_disable(); //processor starts here when any interrupt occurs
|
||||
}
|
||||
unsigned long WDTmicrosTime = micros() - startTime; // this is your measurement!
|
||||
return (float)WDTmicrosTime * 0.0026 - 3668.1; //calibrate here
|
||||
}
|
||||
*/
|
||||
#define SAFE 1
|
||||
|
||||
void setup()
|
||||
|
@ -1442,10 +1358,6 @@ void setup()
|
|||
lcd.begin(16, 2);
|
||||
lcd.createChar(1, font_run);
|
||||
|
||||
//PCICR |= (1 << PCIE0);
|
||||
//PCMSK0 |= (1 << PCINT5) | (1 << PCINT4) | (1 << PCINT3);
|
||||
//interrupts();
|
||||
|
||||
// initialize LUT
|
||||
//#define C31_IS_INSTALLED 1 // Uncomment this line when C31 is installed (shaping circuit will be driven with analog signal instead of being switched digitally with PWM signal)
|
||||
#ifdef C31_IS_INSTALLED // In case of analog driven shaping circuit:
|
||||
|
@ -1488,9 +1400,6 @@ void setup()
|
|||
lcd.setCursor(8, 0); lcd.print("R"); lcd.print(VERSION); lcd.print(blanks);
|
||||
//lcd.setCursor(0, 0); lcd.print(String("QCX-") + (String[]){"SSB", "DSP", "SDR" }[dsp_cap] + F(" R") + F(VERSION) + blanks );
|
||||
|
||||
//lcd.setCursor(0, 1); lcd.print("temp="); lcd.print(getTemp()); lcd.print("C");
|
||||
//for(;!digitalRead(BUTTONS);) wdt_reset();
|
||||
|
||||
#ifdef SAFE
|
||||
// Measure CPU loads
|
||||
if(!(load_tx < 100.0))
|
||||
|
@ -1498,17 +1407,23 @@ void setup()
|
|||
lcd.setCursor(0, 1); lcd.print("!!CPU_tx="); lcd.print(load_tx); lcd.print("%"); lcd.print(blanks);
|
||||
delay(1500); wdt_reset();
|
||||
}
|
||||
for(i = 0; i != 8; i++){
|
||||
if(!(load_rx[i] < 100.0))
|
||||
{
|
||||
lcd.setCursor(0, 1); lcd.print("!!CPU_rx"); lcd.print(i); lcd.print("="); lcd.print(load_rx[i]); lcd.print("%"); lcd.print(blanks);
|
||||
delay(1500); wdt_reset();
|
||||
}
|
||||
|
||||
{
|
||||
lcd.setCursor(0, 1); lcd.print("!!CPU_rx"); lcd.print(0); lcd.print("="); lcd.print(load_rx[0]); lcd.print("%"); lcd.print(blanks);
|
||||
delay(1500); wdt_reset();
|
||||
}
|
||||
//if(!(load_rx_avg < 100.0))
|
||||
if(!(load_rx_avg < 100.0))
|
||||
{
|
||||
lcd.setCursor(0, 1); lcd.print("!!CPU_rx"); lcd.print("="); lcd.print(load_rx_avg); lcd.print("%"); lcd.print(blanks);
|
||||
delay(1500); wdt_reset();
|
||||
// and specify indivual timings for each of the eight alternating processing functions:
|
||||
for(i = 1; i != 8; i++){
|
||||
if(!(load_rx[i] < 100.0))
|
||||
{
|
||||
lcd.setCursor(0, 1); lcd.print("!!CPU_rx"); lcd.print(i); lcd.print("="); lcd.print(load_rx[i]); lcd.print("%"); lcd.print(blanks);
|
||||
delay(1500); wdt_reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure VDD (+5V); should be ~5V
|
||||
|
@ -1582,8 +1497,7 @@ void setup()
|
|||
}
|
||||
#endif
|
||||
|
||||
// Display banner
|
||||
//delay(800); wdt_reset();
|
||||
//delay(800); wdt_reset(); // Display banner
|
||||
lcd.setCursor(7, 0); lcd.print("\001"); lcd.print(blanks);
|
||||
|
||||
volume = (dsp_cap) ? ((dsp_cap == SDR) ? 8 : 14) : 0;
|
||||
|
@ -1637,13 +1551,15 @@ void loop()
|
|||
att_enable = !att_enable;
|
||||
lcd.setCursor(0, 1); lcd.print("Attenuate: "); lcd.print(att_enable ? "ON" : "OFF"); lcd.print(blanks);
|
||||
txen(false); // submit attenuator setting
|
||||
wdt_reset(); delay(1500); wdt_reset();
|
||||
change = true; // refresh display
|
||||
}
|
||||
break;
|
||||
case BL|DC: powerDown(); break;
|
||||
case BL|PL:
|
||||
//calibrate_predistortion();
|
||||
//powermeter();
|
||||
test_tx_amp();
|
||||
powermeter();
|
||||
//test_tx_amp();
|
||||
break;
|
||||
case BL|PT: break;
|
||||
case BR|SC:
|
||||
|
|
Ładowanie…
Reference in New Issue