Removal of RX DC filter. Fixed s-meter error. Added experimental auto ADC biasing mechanism. Added 500 Hz CW filter, added CW freq-offset and improved CW dynamic-range. Fix installation description.

experimental
guido 2019-12-17 13:24:14 +01:00
rodzic 6fb68b9395
commit 8903bf4cfb
2 zmienionych plików z 168 dodań i 121 usunięć

Wyświetl plik

@ -6,28 +6,6 @@
#define VERSION "1.01s"
/* BACKLOG:
code definitions and re-use for comb, integrator, dc decoupling, arctan
in func_ptr for different mode types
refactor main()
agc based on rms256, agc/smeter after filter
noisefree integrator (rx audio out) in lower range
raised cosine tx amp for cw
auto paddle
cw tx message/cw encoder
32 bin fft
dynamic range cw
att extended agc
configurable F_CPU
CW-R/CW-L offset
VFO-A/B+split+RIT
OLED display support
VOX integration in main loop
auto-bias for AUDIO1+2 inputs
K2/TS480 CAT control
faster RX-TX switch to support CW
*/
// QCX pin defintion
#define LCD_D4 0 //PD0
#define LCD_D5 1 //PD1
@ -47,7 +25,7 @@ faster RX-TX switch to support CW
#define AUDIO2 PIN_A1 //PC1
#define DVM PIN_A2 //PC2
#define BUTTONS PIN_A3 //PC3
#define LCD_RS 18 //shared with SDA
#define LCD_RS 18 //PC4 shared with SDA
#define SDA 18 //PC4
#define SCL 19 //PC5
@ -688,7 +666,7 @@ volatile int8_t volume = 8;
volatile bool agc = true;
volatile uint8_t nr = 0;
volatile uint8_t att = 0;
volatile uint8_t att2 = 2;
volatile uint8_t att2 = 3;
volatile uint8_t _init;
//static uint32_t gain = 1024;
@ -767,10 +745,10 @@ void junk()
}
#endif
#define N_FILT 6
#define N_FILT 7
volatile int8_t filt = 0;
inline int16_t filt_var(int16_t v)
inline int16_t filt_var(int16_t v) //filters build with www.micromodeler.com
{
int16_t zx0 = v;
@ -792,9 +770,10 @@ inline int16_t filt_var(int16_t v)
case 1: break; //0-4000Hz (pass-through)
case 2: zx0=(10*(zx0+2*za1+za2)+16*zb1-17*zb2)/32; break; //0-2500Hz elliptic -60dB@3kHz
case 3: zx0=(7*(zx0+2*za1+za2)+48*zb1-18*zb2)/32; break; //0-1700Hz elliptic
case 4: zx0=(5*(zx0-2*za1+za2)+105*zb1-58*zb2)/64; break; //650-840Hz
case 5: zx0=(3*(zx0-2*za1+za2)+108*zb1-61*zb2)/64; break; //650-750Hz
case 6: zx0=((2*zx0-3*za1+2*za2)+111*zb1-62*zb2)/64; break; //630-680Hz
case 4: zx0=(zx0+2*za1+za2+41*zb1-23*zb2)/32; break; //500-1000Hz
case 5: zx0=(5*(zx0-2*za1+za2)+105*zb1-58*zb2)/64; break; //650-840Hz
case 6: zx0=(3*(zx0-2*za1+za2)+108*zb1-61*zb2)/64; break; //650-750Hz
case 7: zx0=((2*zx0-3*za1+2*za2)+111*zb1-62*zb2)/64; break; //630-680Hz
}
zb2=zb1;
zb1=zx0;
@ -804,9 +783,10 @@ inline int16_t filt_var(int16_t v)
case 1: break; //0-4000Hz (pass-through)
case 2: zx0=(8*(zx0+zb2)+13*zb1-43*zc1-52*zc2)/64; break; //0-2500Hz elliptic -60dB@3kHz
case 3: zx0=(4*(zx0+zb1+zb2)+22*zc1-47*zc2)/64; break; //0-1700Hz elliptic
case 4: zx0=((zx0+2*zb1+zb2)+97*zc1-57*zc2)/64; break; //650-840Hz
case 5: zx0=((zx0+zb1+zb2)+104*zc1-60*zc2)/64; break; //650-750Hz
case 6: zx0=((zb1)+109*zc1-62*zc2)/64; break; //630-680Hz
case 4: zx0=(16*(zx0-2*zb1+zb2)+105*zc1-52*zc2)/64; break; //500-1000Hz
case 5: zx0=((zx0+2*zb1+zb2)+97*zc1-57*zc2)/64; break; //650-840Hz
case 6: zx0=((zx0+zb1+zb2)+104*zc1-60*zc2)/64; break; //650-750Hz
case 7: zx0=((zb1)+109*zc1-62*zc2)/64; break; //630-680Hz
}
zc2=zc1;
zc1=zx0;
@ -850,19 +830,38 @@ void sdr_rx()
adc = corr_adc;
static int16_t dc;
dc += (adc - dc) / 2;
int16_t ac = adc - dc; // DC decoupling
//dc += (adc - dc) / 2; // we lose LSB with this method
//dc = (3*dc + adc)/4;
//int16_t ac = adc - dc; // DC decoupling
int16_t ac = adc;
//#define AUTO_ADC_BIAS 1
#ifdef AUTO_ADC_BIAS
param_b = (127*param_b + adc)/128;
if(param_b > 8){
digitalWrite(AUDIO1, LOW);
pinMode(AUDIO1, OUTPUT);
pinMode(AUDIO1, INPUT);
//param_b = 0;
} //else
if(param_b < -8){
digitalWrite(AUDIO1, HIGH);
pinMode(AUDIO1, OUTPUT);
pinMode(AUDIO1, INPUT);
//param_b = 0;
}
#endif
int16_t ac2;
static int16_t z1;
if(rx_state == 0 || rx_state == 4){ // 1st stage: down-sample by 2
static int16_t za1;
int16_t _ac = ac + za1 + z1; // 1st stage: FA + FB
int16_t _ac = ac + za1 + z1 * 2; // 1st stage: FA + FB
za1 = ac;
static int16_t _z1;
if(rx_state == 0){ // 2nd stage: down-sample by 2
static int16_t _za1;
ac2 = _ac + _za1 + _z1; // 2nd stage: FA + FB
ac2 = _ac + _za1 + _z1 * 2; // 2nd stage: FA + FB
_za1 = _ac;
{
// post processing I and Q (down-sampled) results
@ -901,7 +900,8 @@ void sdr_rx()
if(filt) ac = filt_var(ac);
}
if(mode == CW){
if(filt) ac = filt_var(ac << 4) << 2; //if(filt) ac = filt_var(ac << 6);
if(filt) ac = filt_var(ac << 0) << 2;
//if(filt) ac = filt_var(ac << 6);
if(cwdec){ // CW decoder enabled?
char ch = cw(ac >> 0);
@ -917,7 +917,12 @@ void sdr_rx()
//static int16_t dc;
//dc += (ac - dc) / 2;
//dc = (15*dc + ac)/16;
//dc = (15*dc + (ac - dc))/16;
//ac = ac - dc; // DC decoupling
ac = min(max(ac, -512), 511);
//ac = min(max(ac, -128), 127);
// Output stage
static int16_t ozd1, ozd2;
@ -932,8 +937,8 @@ void sdr_rx()
#endif
ozd1 = ac;
}
} else _z1 = _ac * 2;
} else z1 = ac * 2;
} else _z1 = _ac;
} else z1 = ac;
rx_state++;
}
@ -949,20 +954,38 @@ void sdr_rx_2()
sdr_rx_common(); //necessary? YES!
#endif
static int16_t dc;
dc += (adc - dc) / 2;
int16_t ac = adc - dc; // DC decoupling
//static int16_t dc;
//dc += (adc - dc) / 2; // we lose LSB with this method
//dc = (3*dc + adc)/4;
//int16_t ac = adc - dc; // DC decoupling
int16_t ac = adc;
#ifdef AUTO_ADC_BIAS
param_c = (127*param_c + adc)/128;
if(param_c > 8){
digitalWrite(AUDIO2, LOW);
pinMode(AUDIO2, OUTPUT);
pinMode(AUDIO2, INPUT);
//param_c = 0;
} //else
if(param_c < -8){
digitalWrite(AUDIO2, HIGH);
pinMode(AUDIO2, OUTPUT);
pinMode(AUDIO2, INPUT);
//param_c = 0;
}
#endif
int16_t ac2;
static int16_t z1;
if(rx_state == 3 || rx_state == 7){ // 1st stage: down-sample by 2
static int16_t za1;
int16_t _ac = ac + za1 + z1; // 1st stage: FA + FB
int16_t _ac = ac + za1 + z1 * 2; // 1st stage: FA + FB
za1 = ac;
static int16_t _z1;
if(rx_state == 7){ // 2nd stage: down-sample by 2
static int16_t _za1;
ac2 = _ac + _za1 + _z1; // 2nd stage: FA + FB
ac2 = _ac + _za1 + _z1 * 2; // 2nd stage: FA + FB
_za1 = _ac;
{
// Process Q (down-sampled) samples
@ -974,9 +997,8 @@ void sdr_rx_2()
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)
}
rx_state = 0; return;
} else _z1 = _ac * 2;
} else z1 = ac * 2;
} else _z1 = _ac;
} else z1 = ac;
rx_state++;
}
@ -993,14 +1015,17 @@ inline void sdr_rx_common()
#ifdef SECOND_ORDER_DUC
if(volume) OCR1AL = min(max((ozi2>>5) + 128, 0), 255); //if(volume) OCR1AL = min(max((ozi2>>5) + ICR1L/2, 0), ICR1L); // center and clip wrt PWM working range
#else
if(volume) OCR1AL = min(max((ozi1>>5) + 128, 0), 255); //if(volume) OCR1AL = min(max((ozi2>>5) + ICR1L/2, 0), ICR1L); // center and clip wrt PWM working range
OCR1AL = (ozi1>>5) + 128;
//if(volume) OCR1AL = min(max((ozi1>>5) + 128, 0), 255); //if(volume) OCR1AL = min(max((ozi2>>5) + ICR1L/2, 0), ICR1L); // center and clip wrt PWM working range
#endif
}
ISR(TIMER2_COMPA_vect) // Timer2 COMPA interrupt
{
func_ptr();
#ifdef DEBUG
numSamples++;
#endif
}
void adc_start(uint8_t adcpin, bool ref1v1, uint32_t fs)
@ -1041,7 +1066,7 @@ void timer1_start(uint32_t fs)
TCCR1A |= (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); // Clear OC1A,OC1B on Compare Match when upcounting. Set OC1A,OC1B on Compare Match when downcounting.
TCCR1B |= (1 << CS10) | (1 << WGM13) | (1 << WGM12); // WGM13: Mode 14 - Fast PWM; CS10: clkI/O/1 (No prescaling)
ICR1H = 0x00;
ICR1L = (float)F_CPU / (float)fs - 0.5; // PWM value range (determines bit-depth and PWM frequency): Fpwm = F_CPU / Prescaler * (1 + TOP)
ICR1L = min(255, (float)F_CPU / (float)fs - 0.5); // PWM value range (fs>78431): Fpwm = F_CPU / [Prescaler * (1 + TOP)]
OCR1AH = 0x00;
OCR1AL = 0x00; // OC1A (SIDETONE) PWM duty-cycle (span defined by ICR).
OCR1BH = 0x00;
@ -1193,14 +1218,14 @@ volatile int32_t freq = 7074000;
int8_t smode = 1;
float dbm_max;
float smeter(float ref = 5.1) //= 10*log(F_SAMP_RX/R/2400) ref to 2.4kHz BW.
float smeter(float ref = 5) //= 10*log(8000/2400)=5 ref to 2.4kHz BW. plus some other calibration factor
{
if(smode == 0){ // none, no s-meter
return 0;
}
float rms = _absavg256 / 256.0; //sqrt(256.0);
if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (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 * (float)(1 << att2) / (1024.0 * (float)R * 100.0 * 120.0 / 1.750);
if(dsp_cap == SDR) rms = (float)rms * 1.1 * (float)(1 << att2) / (1024.0 * (float)R * 4.0 * 100.0 * 40.0); // rmsV = ADC value * AREF / [ADC DR * processing gain * receiver gain * audio gain]
else rms = (float)rms * 5.0 * (float)(1 << att2) / (1024.0 * (float)R * 2.0 * 100.0 * 120.0 / 1.750);
float dbm = (10.0 * log10((rms * rms) / 50.0) + 30.0) - ref; //from rmsV to dBm at 50R
dbm_max = max(dbm_max, dbm);
static uint8_t cnt;
@ -1532,6 +1557,28 @@ void show_banner(){
lcd.print(F("\x01 ")); lcd_blanks();
}
const char* mode_label[5] = { "LSB", "USB", "CW ", "AM ", "FM " };
void display_vfo(uint32_t freq){
lcd.setCursor(0, 1);
lcd.print('\x06'); // VFO
uint32_t n = freq / 1000000; // lcd.print(f) with commas
uint32_t n2 = freq % 1000000;
uint32_t scale = 1000000;
char buf[16];
sprintf(buf, "%2u", n); lcd.print(buf);
while(scale != 1){
scale /= 1000;
n = n2 / scale;
n2 = n2 % scale;
if(scale == 1) sprintf(buf, ",%02u", n / 10); else // leave last digit out
sprintf(buf, ",%03u", n);
lcd.print(buf);
}
lcd.print(" "); lcd.print(mode_label[mode]); lcd.print(" ");
lcd.setCursor(15, 1); lcd.print("R");
}
volatile uint8_t event;
volatile uint8_t menumode = 0; // 0=not in menu, 1=selects menu item, 2=selects parameter value
volatile int8_t menu = 0; // current parameter id selected in menu
@ -1588,8 +1635,7 @@ static uint8_t pwm_min = 0; // PWM value for which PA reaches its minimum: 29
static uint8_t pwm_max = 192; // PWM value for which PA reaches its maximum: 96 when C31 installed; 255 when C31 removed; x for biasing BS170 directly
const char* offon_label[2] = {"OFF", "ON"};
const char* mode_label[5] = { "LSB", "USB", "CW ", "AM ", "FM " };
const char* filt_label[7] = { "Full", "4000", "2500", "1700", "200", "100", "50" };
const char* filt_label[N_FILT+1] = { "Full", "4000", "2500", "1700", "500", "200", "100", "50" };
const char* band_label[N_BANDS] = { "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m", "4m" };
#define _N(a) sizeof(a)/sizeof(a[0])
@ -1938,7 +1984,7 @@ void loop()
if(!menumode){
encoder_val = 1;
paramAction(UPDATE, MODE); // Mode param //paramAction(UPDATE, mode, NULL, F("Mode"), mode_label, 0, _N(mode_label), true);
if(mode != CW) stepsize = STEP_1k; else stepsize = STEP_100;
if(mode != CW) stepsize = STEP_1k; else stepsize = STEP_500;
//if(mode > FM) mode = LSB;
if(mode > CW) mode = LSB; // skip all other modes (only LSB, USB, CW)
if(mode == CW) filt = 4; else filt = 0;
@ -2050,10 +2096,11 @@ void loop()
if(menumode == 2){
lcd.setCursor(0, 1); lcd.cursor(); delay(10); // edits menu item value; make cursor visible
if(menu == MODE){ // post-handling Mode parameter
delay(100);
change = true;
si5351.prev_pll_freq = 0; // enforce PLL reset
// make more generic:
if(mode != CW) stepsize = STEP_1k; else stepsize = STEP_100;
if(mode != CW) stepsize = STEP_1k; else stepsize = STEP_500;
if(mode == CW) filt = 4; else filt = 0;
}
if(menu == ATT){ // post-handling ATT parameter
@ -2084,6 +2131,9 @@ void loop()
for(i = 0; i != 300000; i++) wdt_reset(); // fixed CPU-load 132052*1.25us delay under 0% load condition; is 132052*1.25 * 20M = 3301300 CPU cycles fixed load
cpu_load = 100 - 132 * 100 / (millis() - prev_time);
}
if((menu == PARAM_A) || (menu == PARAM_B) || (menu == PARAM_C)){
delay(300);
}
#endif
}
}
@ -2101,23 +2151,7 @@ void loop()
schedule_time = millis() + 1000; // schedule time to save freq (no save while tuning, hence no EEPROM wear out)
if(menumode == 0){
lcd.setCursor(0, 1);
lcd.print('\x06'); // VFO
uint32_t n = freq / 1000000; // lcd.print(f) with commas
uint32_t n2 = freq % 1000000;
uint32_t scale = 1000000;
char buf[16];
sprintf(buf, "%2u", n); lcd.print(buf);
while(scale != 1){
scale /= 1000;
n = n2 / scale;
n2 = n2 % scale;
if(scale == 1) sprintf(buf, ",%02u", n / 10); else // leave last digit out
sprintf(buf, ",%03u", n);
lcd.print(buf);
}
lcd.print(" "); lcd.print(mode_label[mode]); lcd.print(" ");
lcd.setCursor(15, 1); lcd.print("R");
display_vfo(freq);
// The following is a hack for SWR measurement:
//si5351.alt_clk2(freq + 2400);
@ -2126,10 +2160,15 @@ void loop()
}
noInterrupts();
if(mode == CW){
const int cw_offset = 600; // this is actual the center frequency of the CW-filters
si5351.freq(freq + cw_offset, 90, 0); // RX in CW-R (=LSB), correct for CW-tone offset
si5351.freq_calc_fast(-cw_offset); si5351.SendPLLBRegisterBulk(); // TX at freq
} else
if(mode == LSB)
si5351.freq(freq, 90, 0); // RX in LSB
else
si5351.freq(freq, 0, 90); // RX in USB
si5351.freq(freq, 0, 90); // RX in USB, ...
interrupts();
}
@ -2149,3 +2188,29 @@ void main()
for(;;) loop();
}
#endif
/* TODO/BACKLOG:
code definitions and re-use for comb, integrator, dc decoupling, arctan
in func_ptr for different mode types
refactor main()
agc based on rms256, agc/smeter after filter
noisefree integrator (rx audio out) in lower range
raised cosine tx amp for cw
auto paddle
cw tx message/cw encoder
32 bin fft
dynamic range cw
att extended agc
configurable F_CPU
CW-L mode
VFO-A/B+split+RIT
OLED display support aka http://www.technoblogy.com/list?22ML
VOX integration in main loop
auto-bias for AUDIO1+2 inputs
K2/TS480 CAT control
faster RX-TX switch to support CW
clock
qcx API demo code
scan
si5351 simplification aka https://groups.io/g/BITX20/files/KE7ER/si5351bx_0_0.ino
*/

Wyświetl plik

@ -15,7 +15,8 @@ pe1nnz@amsat.org
![](top.png)
## List of features:
- Modification into a **simple, fun and versatile QRP CW/SSB HF transceiver** with some interesting DSP and SDR techniques; it compromises a bit on performance so not a high-performance transceiver.
- Modification into a **simple, fun and versatile QRP SSB HF transceiver** with embedded DSP and SDR functions;
- High-performance QSD mixer with steep baseband roll-off followed by a digital signal processing stage provides a good operational performance in terms of sensitivity, selectivity, spurious rejection and overall (blocking) dynamic range.
- **[EER]/[Polar-transmitter] Class-E** driven SSB transmit-stage
- Approximately **5W PEP SSB output** (depending on supply voltage, amplitude-PA voltage regulated through PWM with 48dB dynamic range)
- Supports **USB and LSB** modes up to **2400 Hz bandwidth** (receiver and transmitter 400..2330Hz)
@ -32,8 +33,8 @@ pe1nnz@amsat.org
- An **pre-distorion** algorithm that cancels out the amplitude errors of non-linearities in the PA voltage regulated PWM supply; a lookup table is used that can be calibrated with an internal PA amplitude measurement
- Possibility to extend the QCX analog phasing stage with a **DSP stage**
- Could replace the QCX analog phasing stage completely with a **digital SDR receiver stage**, taking away the need for the manual side-band rejection adjustment procedure and delivering DSP features such as the joy of having a **AGC, adjustable CW/SSB filters**
- Receiver Noise floor **(MDS): –130 dBm** at 24MHz (in 500Hz BW); Blocking dynamic range: 148dB (200kHz offset or greater), 118dB (20kHz offset), 78dB (2kHz offset). Blocking: 18dBm (200kHz offset or greater), -12dBm (20kHz offset), -52dBm (2kHz offset) (IC6A/B-gain must reduce with -10dB!!); LO Phase noise: -138dBc/Hz; Front-end selectivity: Octave
- Digitally switchable RF front-end **attenuators (0dB, -13dB, -20dB, -33dB, -53dB, -60dB, -73dB)**
- Receiver Noise floor **(MDS): –130 dBm** at 24MHz (in 500Hz BW); Blocking dynamic range: 148dB (200kHz offset or greater), 118dB (20kHz offset), 78dB (2kHz offset). Blocking: 18dBm (200kHz offset or greater), -12dBm (20kHz offset), -52dBm (2kHz offset) _(IC6A/B-gain must reduce with -18 (or maybe even -22) dB!!)_; LO Phase noise: -138dBc/Hz; Front-end selectivity: Octave
- three independent switchable front-end **attenuators (0dB, -13dB, -20dB, -33dB, -53dB, -60dB, -73dB)**
- This firmware is compatible with an unmodified QCX, partial modified QCX (e.g. SSB mod, or DSP mod), or fully modified QCX (SSB + SDR mod, as described below)
- SDR implementation simplifies the receiver heaviliy; **the original QCX PCB is now half empty** by moving the analog processing into the microcontroller and adding new and improving existing features. On a new QCX build: 46 components less to be installed, 8 component design changes, 9 additional wires.
- Probably the most cost effective and easiest to build standalone SDR transceiver that you can find. More versatile and easier to build than the original QCX (less components, no transformer windings, no alignment procedure)
@ -42,8 +43,8 @@ pe1nnz@amsat.org
## Revision History:
| Rev. | Date | Features |
| ----- | ---------- | ------------------------------------------------------------------- |
| R1.02 (**this**) | 2019-09-01 | Integrated SDR receiver, CW decoder, DSP filters, AGC, NR, ATT, experimental modes CW, AM, FM, quick menu, persistent settings. |
| R1.01d | 2019-05-05 | Q6 now digitally switched (remove C31) - improving stability and IMD. Improved signal processing, audio quality, increased bandwidth, cosmetic changes and reduced RF feedback, reduced s-meter RFI, S-meter readings, self-test on startup. Receiver I/Q calibration, (experimental) amplitude pre-distortion and calibration. |
| R1.02 (**current**) | 2019-12-22 | Integrated SDR receiver, CW decoder, DSP filters, AGC, NR, ATT, experimental modes CW, AM, FM, quick menu, persistent settings. |
| R1.01d | 2019-05-05 | Q6 now digitally switched (remove C31) - improving stability and IMD. Improved signal processing, audio quality, increased bandwidth, cosmetic changes and reduced RF feedback, reduced s-meter RFI, S-meter readings, self-test on startup. Receiver I/Q calibration, (experimental) amplitude pre-distortion and calibration. **See here [original QCX-SSB modification] (it is also supported by current firmware)** |
| R1.00 | 2019-01-29 | Initial release of SSB transceiver prototype. |
@ -53,19 +54,21 @@ Below the schematic after the modification is applied, unused components are lef
## Installation:
To make the SDR+SSB modification, you need to remove 9 and change 8 components, install 10 wires, upload firmware and connect a microphone. On a newly to be build QCX, 46 components can be left out.
If you just want to try out the firmware, you can upload and use it in an unmodified QCX but it will have the SSB and SDR features disabled. If you like to try out the DSP audio processing feature, you can simply disconnect R59 and hook up a speaker on pin15/U2 (with 10uF in series, similar as shown above).
**Note: Click here for the [original QCX-SSB modification]. The orginal mod is supported by the latest firmware and optionally a DSP audio processing feature can be enabled by disconnecting R59 and hooking up a speaker on pin15/U2 (via 10uF capacitor, similar as shown above).**
To make the SDR+SSB modification, you need to remove 9 and change 8 components, install 10 wires, upload firmware and connect a microphone. On a newly to be build QCX, 46 components can be left out.
Change the following component values (and type of component in some cases), and wire the following component pins on the backside PCB (some pins must be disconnected from the pad):
1. To implement the SDR receiver: R11,12,17,24,27,29,59,IC10 (remove); IC7-9,R13,R18-20,R25,R28-40,R60,C9,C11,C13-24,C52-53,D5,Q7 (omit on new builds); C10 (.1uF); R16,23 (120k); wire IC10(pin7) to IC6(pin7); wire R27(pin2) to IC6(pin1); wire IC2(pin15) to IC10(pin1); disconnect R50-5V and R52-5V and both wire to R57-DVM(pin3); disconnect R21-IC6(pin7) and R22-IC6(pin7) and both wire to R7-IC5(pin1).
2. To implement the SSB transmitter: D4,R21,R56 (10k); R58 (.22uF); C32 (10uF); C31 (remove); wire IC2-pin21 to R57-DVM(pin3); wire IC2(pin20) to DVM(pin2); wire IC2(pin18) to junction D4-C42-R58;
1. To implement the SDR receiver: R11,12,17,24,27,29,59,IC10 (remove); IC7-10,R11-13,R17-20,R24-25,R28-40,R59,R60,C9,C11,C13-24,C52-53,D5,Q7 (omit on new builds); change C10 (.1uF); R16,23 (120k); R21 (10k); wire IC10(pin7) to IC6(pin7); wire R27(pin2) to IC6(pin1); wire IC2(pin15) to IC10(pin1); disconnect pin R50(to 5V) and pin R52(to 5V) and both wire to IC2(pin21); disconnect pin R21(to IC6-pin7) and pin R22(to IC6-pin7) and both wire to pin R7(to IC5-pin1).
_Rationale: This will feed the I/Q signals to the ADC0, ADC1 input, biased at AREF/2 V, the rest of the receiver will be handled in software and audio output is realised on PB1._
2. To implement the SSB transmitter: change D4,R56 (10k); R58 (.22uF); C32 (10uF); C31 (remove); wire IC2(pin21) to pin R57(to DVM-pin3); wire IC2(pin20) to DVM(pin2); wire IC2(pin18) to junction D4-C42-R58.
_Rationale: This will bias the mic input (at DAH line) with 5V and pass the audio to ADC2, biased at AREF/2 V; the key-shaping circuit is digitally switching the voltage supply to the PA (or alternatively directly controlled via PA bias<sup>[note 3](#note3)</sup>)._
3. To implement multiband support: C1,C5,C8,T1,R64 (remove); at T1 landing pattern (see [QCX Assembly instruction] page 53) install R (1K) over 6-8; R (1K) over 3-4; C (10nF) over 4-8; C30 (30pF); L4 (1uH/16t); replace C25-28,L1-L3 with different LPFs as you wish.
4. Upload the hex firmware-file to existing or new ATMEGA328/328P chip (here is [latest released hex file] and click on "Assets" below the description). The [standard QCX firmware upload procedure] can be followed (for details <sup>[note 1](#note1)</sup>). You can safely switch between this/original QCX firmware without any issues.
_Rationale: The resonant elements and the transformer are replaced with a pass-through capacitor._
4. Upload the hex firmware-file to original or new ATMEGA328/328P chip (here is [latest released hex file] and click on "Assets" below the description). The [standard QCX firmware upload procedure] can be followed (for details <sup>[note 1](#note1)</sup>). You can safely switch between this/original QCX firmware without any issues.
5. Connect an electret microphone pins (+) to tip and (-) to sleeve of paddle-jack; PTT-switch pins to ring and sleeve (see [X1M-mic]).
Below the layout with components marked in red that needs to be changed; gray components must be installed and blank components may be omitted and some must be remove (see above):
![layout](layout.png)
@ -74,46 +77,22 @@ Below the wires that needs to be installed on the bottom PCB; a circle indicates
## Operation:
Currently, the following functions have been assigned to the buttons:
Currently, the following functions have been assigned to shortcut buttons (L=left, E=encoder, R=right) and menu-items:
| Button | Function |
| ------------------- | ------------------------------------------------------- |
| LEFT single-press | Selects menu-item, setting or goes back (optional: CENTER, RIGHT click) |
| LEFT double-press | |
| LEFT long-press | |
| LEFT push + turn | Quick menu |
| CENTER single-press | Select (smaller) frequency step |
| CENTER double-press | Select Band |
| CENTER long-press | Select (larger) frequency step |
| CENTER turn | Tune frequency |
| CENTER push + turn | Volume & Power-off/on |
| RIGHT single-press | LSB/USB/CW-mode |
| RIGHT double-press | Filter Bandwidth |
| RIGHT long-press | VOX mode (for full-break-in or digital modes) |
| RIGHT push + turn | |
| KEY | Push-to-talk (SSB) / Straight-key (CW) |
Currently, the following functions have been assigned to buttons and menu-items:
| Menu Item | Function | Button |
| ------------------- | -------------------------------------------- | ----------- |
| | Push-to-talk (SSB) / Straight-key (CW) | **KEY**
| | Quick menu | **LEFT push + turn** |
| | Menu-item | **LEFT single-press** |
| | Tune frequency | **CENTER turn** |
| 1.1 Volume | Audio level (0..16) & power-off/on | **CENTER push + turn** |
| 1.2 Mode | Modulation (LSB, USB, CW, AM, FM) | **RIGHT single-press** |
| 1.3 Filter BW | Audio passband (Full, 300..4000, 300..2500, 300..1700, 200, 100 Hz) | **RIGHT double-press** |
| 1.4 Band | Band-switch to pre-defined FT8 freqs for 80m, 60m, 40m, 30m, 20m, 17m, 15m, 12m, 10m, 6m, 4m | **CENTER double-press** |
| 1.5 Tuning Rate | Tuning frequency step size 10M, 1M, 0.5M, 100k, 10k, 1k, 0.5k, 100, 10, 1 | **CENTER long/single-press** |
| Menu Item | Function | Shortcut |
| ------------------- | -------------------------------------------- | -------- |
| 1.1 Volume | Audio level (0..16) & power-off/on | **E +turn** |
| 1.2 Mode | Modulation (LSB, USB, CW, AM, FM) | **R** |
| 1.3 Filter BW | Audio passband (Full, 300..4000, 300..2500, 300..1700, 200, 100 Hz) | **R double** |
| 1.4 Band | Band-switch to pre-defined FT8 freqs (80,60,40,30,20,17,15,12,10,6,4m) | **E double** |
| 1.5 Tuning Rate | Tuning step size 10M, 1M, 0.5M, 100k, 10k, 1k, 0.5k, 100, 10, 1 | **E or E long** |
| 1.6 AGC | Automatic Gain Control (ON, OFF) | |
| 1.7 NR | Noise-reduction level (0-8), basically smooth signal and cut-off high-frequencies | |
| 1.7 NR | Noise-reduction level (0-8), load-pass & smooth | |
| 1.8 ATT | Analog Attenuator (0, -13, -20, -33, -40, -53, -60, -73 dB) | |
| 1.9 ATT2 | Digital Attenuator in CIC-stage (0-16) in steps of 6dB | |
| 1.10 S-meter | Type of S-Meter (OFF, dBm, S, S-bar) | |
| 2.1 CW Decoder | Enable/disable CW Decoder (ON, OFF) | |
| 3.1 VOX | Voice Operated Xmit (ON, OFF) | **RIGHT long-press** | |
| 3.1 VOX | Voice Operated Xmit (ON, OFF) | **R long** | |
| 3.2 VOX Level | Audio threshold of VOX (0-255) | |
| 3.3 MOX | Monitor on Xmit (audio unmuted during transmit) | |
| 3.4 TX Drive | Transmit audio gain (0-8) in steps of 6dB, 8=constant amplitude for SSB | |
@ -125,7 +104,10 @@ Currently, the following functions have been assigned to buttons and menu-items:
| 9.3 Param A | for debugging, testing and experimental purpose | |
| 9.4 Param B | for debugging, testing and experimental purpose | |
| 9.5 Param C | for debugging, testing and experimental purpose | |
| main | Frequency (20kHz..99MHz) | **turn** |
| main | Quick menu | **L +turn** |
| main | Menu enter | **L** |
| menu | Menu back | **R** |
Operating Instructions:
@ -181,7 +163,7 @@ The following performance measurements were made with QCX-SSB R1.01, a modified
- Alternatively, in case you have an [Arduino] environment installed, you can upload the [QCX-SSB Sketch] directly from the Arduino environment (without using AVRDudess and firmware file); make sure "Tools > Board > Arduino/Genuino Uno", "Tools > Port > /dev/ttyUSB0 or ttyACM0", and then "Sketch > Upload" is selected, while the ATMEGA328P chip is placed in the Arduino UNO socket. It is also possible to use [Arduino as ISP] method: upload this variation of [ArduinoISP] to the Arduino board and select "Tools > Programmer > Arduino as ISP", and "Sketch > Upload Using Programmer".
2. <a name="note2"/>The occupied SSB bandwidth can be further reduced by restricting the maximum phase change (set MAX_DP to half a unit-circle _UA/2 (equivalent to 180 degrees)). The sensitivity of the VOX switching can be set with parameter VOX_THRESHOLD. Audio-input can be attenuated by increasing parameter MIC_ATTEN (6dB per step).
3. Alternatively, the PA MOSFETs can be directly biased by the PWM envelope signal, basically making the key-shaping circuit redundant. To do so, Q6,Q4,R41,R42,C32,C31 can be removed entirely, whereby C-E pads of Q6 are wired, and where a 100nF capacitor is inserted at IC3A-pin3 and G of Q1-3, and where a 10k resistor is placed at G-D pads of Q4, a 10nF capacitor between S-D pads of Q4, and where a 10k resistor is placed between D of Q4 and G of Q1-3.
4. Experimental alternative for 18dB less overall receiver gain and a single receiver gain stage only: To implement the SDR receiver: R11,12,R14,R15,17,59 (remove); change R7,10(47k); change C4,C7(2nF); IC6-10,R11-40,R59-60,C9-24,C52-53,D5,Q7 (omit on new builds); wire IC2(pin15) to IC10(pin1); disconnect R50-(to 5V) pin and R52-5V and both wire to IC2(pin25); disconnect pin C39(to R27) and wire to IC5(pin1); disconnect pin C40(-to R27) and wire to IC5(pin7).
### Credits:
[QCX] (QRP Labs CW Xcvr) is a kit designed by _Hans Summers (G0UPL)_, originally built for RSGB's YOTA summer camp 2017, a high performance, image rejecting DC transceiver; basically a simplified implementation of the [NorCal 2030] by _Dan Tayloe (N7VE)_ designed in 2004 combined with a [Hi-Per-Mite] Active Audio CW Filter by _David Cripe (NMØS)_, [Low Pass Filters] from _Ed (W3NQN)_ 1983 Articles, a key-shaping circuit by _Donald Huff (W6JL)_, a BS170 switched [CMOS driven MOSFET PA] architecture as used in the [ATS] designs by _Steven Weber (KD1JV)_ (originating from the [Power MOSFET revolution] in the mid 70s), and combined with popular components such as Atmel [ATMEGA328P] microprocessor, a Hitachi [HD44780] LCD display and a Silicon Labs [SI5351] Clock Generator (and using a [phase shift in the SI5351 clocks]). The [QCX-SSB] transmitter and QCX-SDR receiver stage both running on a ATMEGA328P, including its multiband front-end and direct PA biasing/envelope-generation technique; its concept, circuit, code and modification to run on a QCX are a design by _Guido (PE1NNZ)_; the software-based SSB transmit stage is a derivate of earlier experiments with a [digital SSB generation technique] on a Raspberry Pi.