*/
cw_changed();
receiver_filter_changed(active_receiver);
+ // changing the side tone frequency affects BFO frequency offsets
+ if (protocol == NEW_PROTOCOL) {
+ schedule_high_priority();
+ }
}
void cw_menu(GtkWidget *parent) {
*
* This device has four "RF sources"
*
- * RF1: ADC noise (16-bit ADC) plus a 800 Hz signal at -100dBm
- * RF2: ADC noise (16-bit ADC) plus a 2000 Hz signal at - 80dBm
+ * RF1: ADC noise plus a 800 Hz signal at -100dBm
+ * RF2: ADC noise
* RF3: TX feedback signal with some distortion.
- * RF4: normalized undistorted TX signal with a peak value of 0.407
+ * RF4: normalized undistorted TX signal
*
* RF1 and RF2 signal strenght vary according to Preamp and Attenuator settings
* RF3 signal strength varies according to TX-drive and TX-ATT settings
- * RF4 signal strength is normalized to amplitude of 0.407
+ * RF4 signal strength is normalized to amplitude of 0.407 (old protocol) or 0.2899 (new protocol)
*
* The connection with the ADCs are:
- * first ADC: RF1 upon receive, RF3 upon transmit
- * second ADC: RF2
+ * ADC0: RF1 upon receive, RF3 upon transmit
+ * ADC1: RF2 (for HERMES: RF4)
+ * ADC2: RF4
*
* RF4 is the TX DAC signal. Upon TX, it goes to RX2 for Metis, RX4 for Hermes, and RX5 beyond.
* Since the feedback runs at the RX sample rate while the TX sample rate is fixed (48000 Hz),
* we have to re-sample and do this in a very stupid way (linear interpolation).
*
+ * The "noise" is a random number of amplitude 0.00003 (last bit on a 16-bit ADC),
+ * that is about -90 dBm spread onto a spectrum whose width is the sample rate. Therefore
+ * the "measured" noise floor in a filter 5 kHz wide is -102 dBm for a sample rate of 48 kHz
+ * but -111 dBm for a sample rate of 384000 kHz. This is a nice demonstration how the
+ * spectral density of "ADC noise" goes down when increasing the sample rate.
+ *
* The SDR application has to make the proper ADC settings, except for STEMlab
* (RedPitaya based SDRs), where there is a fixed association
* RX1=ADC1, RX2=ADC2, RX3=ADC2, RX4=TX-DAC
* Audio sent to the "radio" is played via the first available output channel.
* This works on MacOS (PORTAUDIO) and Linux (ALSASOUND).
*
- * Additional feature include the recording of the TX envelope of the first second
- * of TXing, and the possiblity to read a file with mic samples and "send"
- * them to the SDR. Both features are meant for testing RX/TX timings.
+ * If invoked with the "-diversity" flag, broad "man-made" noise is fed to ADC1 and
+ * ADC2 upon RXing. The ADC2 signal is phase shifted by 90 degrees and somewhat
+ * stronger. This noise can completely be eliminated using DIVERSITY.
*/
#include <stdio.h>
#include <errno.h>
void new_protocol_general_packet(unsigned char *buffer);
int new_protocol_running(void);
-#define LENNOISE 192000
-#define NOISEDIV (RAND_MAX / 96000)
+#define LENNOISE 1536000
+#define NOISEDIV (RAND_MAX / 768000)
double noiseItab[LENNOISE];
double noiseQtab[LENNOISE];
+int diversity=0;
+
+#define LENDIV 16000
+double divtab[LENDIV];
+//
+// The tone is recorded with a sample rate of 1536 kHz. For lower TX
+// sample rates, one has to decimate it
+//
+#define LENTONE 15360
+double toneItab[LENTONE];
+double toneQtab[LENTONE];
+
+double c1,c2; // shared. needed for power conversion
/*
* These variables store the state of the SDR.
* Whenevery they are changed, this is reported.
static double last_i_sample=0.0;
static double last_q_sample=0.0;
static int txptr=0;
-static int rxptr=0;
//
// Unfortunately, the code number of the gear
pthread_attr_t attr;
pthread_t thread;
- uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
+ uint8_t reply[11] = { 0xef, 0xfe, 2, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 32, 1 };
uint8_t id[4] = { 0xef, 0xfe, 1, 6 };
uint32_t code;
uint32_t *code0 = (uint32_t *) buffer; // fast access to code of first buffer
int fd;
long cnt;
+ double run,off,inc;
+
+/*
+ * Examples for ATLAS: ATLAS bus with Mercury/Penelope boards
+ * Examples for HERMES: ANAN10, ANAN100
+ * Examples for ANGELIA: ANAN100D
+ * Examples for ORION: ANAN200D
+ * Examples for ORION2: ANAN7000, ANAN8000
+ *
+ * Examples for C25: RedPitaya based boards with fixed ADC connections
+ */
+ for (i=1; i<argc; i++) {
+ if (!strncmp(argv[i],"-atlas" , 6)) {OLDDEVICE=DEVICE_ATLAS; NEWDEVICE=NEW_DEVICE_ATLAS;}
+ if (!strncmp(argv[i],"-hermes" , 7)) {OLDDEVICE=DEVICE_HERMES; NEWDEVICE=NEW_DEVICE_HERMES;}
+ if (!strncmp(argv[i],"-hermes2" , 8)) {OLDDEVICE=DEVICE_HERMES2; NEWDEVICE=NEW_DEVICE_HERMES2;}
+ if (!strncmp(argv[i],"-angelia" , 8)) {OLDDEVICE=DEVICE_ANGELIA; NEWDEVICE=NEW_DEVICE_ANGELIA;}
+ if (!strncmp(argv[i],"-orion" , 6)) {OLDDEVICE=DEVICE_ORION; NEWDEVICE=NEW_DEVICE_ORION;}
+ if (!strncmp(argv[i],"-orion2" , 7)) {OLDDEVICE=DEVICE_ORION2; NEWDEVICE=NEW_DEVICE_ORION2;}
+ if (!strncmp(argv[i],"-hermeslite" , 11)) {OLDDEVICE=DEVICE_HERMES_LITE; NEWDEVICE=NEW_DEVICE_HERMES_LITE;}
+ if (!strncmp(argv[i],"-c25" , 4)) {OLDDEVICE=DEVICE_C25; NEWDEVICE=NEW_DEVICE_HERMES;}
+ if (!strncmp(argv[i],"-diversity", 10)) {diversity=1;}
+ }
+
+ switch (OLDDEVICE) {
+ case DEVICE_ATLAS: fprintf(stderr,"DEVICE is ATLASS\n"); c1=3.3; c2=0.090; break;
+ case DEVICE_HERMES: fprintf(stderr,"DEVICE is HERMES\n"); c1=3.3; c2=0.095; break;
+ case DEVICE_HERMES2: fprintf(stderr,"DEVICE is HERMES (2)\n"); c1=3.3; c2=0.095; break;
+ case DEVICE_ANGELIA: fprintf(stderr,"DEVICE is ANGELIA\n"); c1=3.3; c2=0.095; break;
+ case DEVICE_ORION: fprintf(stderr,"DEVICE is ORION\n"); c1=5.0; c2=0.108; break;
+ case DEVICE_ORION2: fprintf(stderr,"DEVICE is ORION-II\n"); c1=5.0; c2=0.108; break;
+ case DEVICE_C25: fprintf(stderr,"DEVICE is STEMlab/C25\n"); c1=3.3; c2=0.090; break;
+ }
+
+ fprintf(stderr,".... producing random noise\n");
// Produce some noise
j=RAND_MAX / 2;
for (i=0; i<LENNOISE; i++) {
noiseQtab[i]= ((double) rand() / j - 1.0) * 0.00003;
}
+ fprintf(stderr,".... producing an 800 Hz signal\n");
+ // Produce an 800 Hz tone at 0 dBm
+ run=0.0;
+ for (i=0; i<LENTONE; i++) {
+ toneQtab[i]=cos(run);
+ toneItab[i]=sin(run);
+ run += 0.0032724923474893679567319201909161;
+ }
+
+ if (diversity) {
+ fprintf(stderr,"DIVERSITY testing activated!\n");
+ fprintf(stderr,".... producing some man-made noise\n");
+ memset(divtab, 0, LENDIV*sizeof(double));
+ for (j=1; j<=200; j++) {
+ run=0.0;
+ off=0.25*j*j;
+ inc=j*0.00039269908169872415480783042290994;
+ for (i=0; i< LENDIV; i++) {
+ divtab[i] += cos(run+off);
+ run += inc;
+ }
+ }
+ // normalize
+ off=0.0;
+ for (i=0; i<LENDIV; i++) {
+ if ( divtab[i] > off) off=divtab[i];
+ if (-divtab[i] > off) off=-divtab[i];
+ }
+ off=1.0/off;
+ fprintf(stderr,"(normalizing with %f)\n",off);
+ for (i=0; i<LENDIV; i++) {
+ divtab[i]=divtab[i]*off;
+ }
+ }
+
memset (isample, 0, RTXLEN*sizeof(double));
memset (qsample, 0, RTXLEN*sizeof(double));
audio_get_cards();
audio_open_output();
-/*
- * Examples for METIS: Mercury/Penelope boards
- * Examples for HERMES: ANAN10, ANAN100
- * Examples for ANGELIA: ANAN100D
- * Examples for ORION: ANAN200D
- * Examples for ORION2: ANAN7000D, ANAN8000D
- */
-
- if (argc > 1) {
- if (!strncmp(argv[1],"-atlas" , 6)) {OLDDEVICE=DEVICE_ATLAS; NEWDEVICE=NEW_DEVICE_ATLAS;}
- if (!strncmp(argv[1],"-hermes" , 7)) {OLDDEVICE=DEVICE_HERMES; NEWDEVICE=NEW_DEVICE_HERMES;}
- if (!strncmp(argv[1],"-hermes2" , 8)) {OLDDEVICE=DEVICE_HERMES2; NEWDEVICE=NEW_DEVICE_HERMES2;}
- if (!strncmp(argv[1],"-angelia" , 8)) {OLDDEVICE=DEVICE_ANGELIA; NEWDEVICE=NEW_DEVICE_ANGELIA;}
- if (!strncmp(argv[1],"-orion" , 6)) {OLDDEVICE=DEVICE_ORION; NEWDEVICE=NEW_DEVICE_ORION;}
- if (!strncmp(argv[1],"-orion2" , 7)) {OLDDEVICE=DEVICE_ORION2; NEWDEVICE=NEW_DEVICE_ORION2;}
- if (!strncmp(argv[1],"-hermeslite" , 11)) {OLDDEVICE=DEVICE_HERMES_LITE; NEWDEVICE=NEW_DEVICE_HERMES_LITE;}
- if (!strncmp(argv[1],"-c25" , 4)) {OLDDEVICE=DEVICE_C25; NEWDEVICE=NEW_DEVICE_HERMES;}
- }
- switch (OLDDEVICE) {
- case DEVICE_ATLAS: fprintf(stderr,"DEVICE is ATLASS\n"); break;
- case DEVICE_HERMES: fprintf(stderr,"DEVICE is HERMES\n"); break;
- case DEVICE_HERMES2: fprintf(stderr,"DEVICE is HERMES (2)\n"); break;
- case DEVICE_ANGELIA: fprintf(stderr,"DEVICE is ANGELIA\n"); break;
- case DEVICE_ORION: fprintf(stderr,"DEVICE is ORION\n"); break;
- case DEVICE_ORION2: fprintf(stderr,"DEVICE is ORION-II\n"); break;
- case DEVICE_C25: fprintf(stderr,"DEVICE is STEMlab/C25\n"); break;
- }
- reply[10]=OLDDEVICE;
if ((sock_udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
return EXIT_FAILURE;
}
- // Fake MAC address
- reply[3]=0xAA;
- reply[4]=0xBB;
- reply[5]=0xCC;
- reply[6]=0xDD;
- reply[7]=0xEE;
- reply[8]=0xFF;
-
setsockopt(sock_udp, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
tv.tv_sec = 0;
switch (code)
{
- // PC to Red Pitaya transmission via process_ep2
+ // PC to SDR transmission via process_ep2
case 0x0201feef:
// processing an invalid packet is too dangerous -- skip it!
fprintf(stderr,"InvalidLength: RvcMsg Code=0x%08x Len=%d\n", code, (int)bytes_read);
break;
}
- reply[2] = 2 + active_thread;
+ reply[ 2] = 2;
+ if (active_thread || new_protocol_running()) {
+ reply[2] = 3;
+ }
+ reply[10] = OLDDEVICE;
memset(buffer, 0, 60);
memcpy(buffer, reply, 11);
break;
- // stop the Red Pitaya to PC transmission via handler_ep6
+ // stop the SDR to PC transmission via handler_ep6
case 0x0004feef:
fprintf(stderr, "STOP the transmission via handler_ep6 / code: 0x%08x\n", code);
}
break;
- // start the Red Pitaya to PC transmission via handler_ep6
- case 0x1104feef:
-
- fprintf(stderr, "TCP METIS-start message received / code: 0x%08x\n", code);
-
- /* FALLTHROUGH */
-
case 0x0104feef:
case 0x0204feef:
case 0x0304feef:
- fprintf(stderr, "START the handler_ep6 thread / code: 0x%08x\n", code);
-
+ if (new_protocol_running()) {
+ fprintf(stderr,"OldProtocol START command received but NewProtocol radio already running!\n");
+ break;
+ }
// processing an invalid packet is too dangerous -- skip it!
if (bytes_read != 64)
{
fprintf(stderr,"InvalidLength: RvcMsg Code=0x%08x Len=%d\n", code, bytes_read);
break;
}
+ fprintf(stderr, "START the PC-to-SDR handler thread / code: 0x%08x\n", code);
enable_thread = 0;
while (active_thread) usleep(1000);
// TX samples sent to the SDR and PURESIGNAL feedback
// samples arriving
//
- txptr=(25 << rate) * 126; // must be even multiple of 63
- rxptr=0;
+ txptr=RTXLEN/2;
memset(isample, 0, RTXLEN*sizeof(double));
memset(qsample, 0, RTXLEN*sizeof(double));
enable_thread = 1;
default:
/*
- * Here we end up with several possible packets from the new protocol
- * These are processed here.
+ * Here we have to handle the following "non standard" cases:
+ * OldProtocol "program" packet
+ * OldProtocol "erase" packet
+ * OldProtocol "Set IP" packet
+ * NewProtocol "Discovery" packet
+ * NewProtocol "program" packet
+ * NewProtocol "erase" packet
+ * NewProtocol "Set IP" packet
+ * NewProtocol "General" packet ==> this starts NewProtocol radio
*/
if (bytes_read == 264 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03 && buffer[3] == 0x01) {
static long cnt=0;
buffer[10]=0xFF;
buffer[11]=NEWDEVICE;
buffer[12]=38;
- buffer[13]=103;
+ buffer[13]=19;
buffer[20]=2;
buffer[21]=1;
buffer[22]=3;
if (seq == 0) checksum=0;
for (j=9; j<=264; j++) checksum += buffer[j];
memset(buffer+4, 0, 56); // keep seq. no
- buffer[4]=0x04;
+ buffer[ 4]=0x04;
buffer [5]=0xAA;
buffer[ 6]=0xBB;
buffer[ 7]=0xCC;
break;
}
if (bytes_read == 60 && buffer[4] == 0x00) {
- // handle general packet
+ // handle "general packet" of the new protocol
memset(&addr_new, 0, sizeof(addr_new));
addr_new.sin_family = AF_INET;
addr_new.sin_addr.s_addr = addr_from.sin_addr.s_addr;
}
}
-static double T0800Itab[480];
-static double T0800Qtab[480];
-static double T2000Itab[192];
-static double T2000Qtab[192];
-
void *handler_ep6(void *arg)
{
int i, j, k, n, size;
#ifdef __APPLE__
struct timespec now;
#endif
- int wait;
- int noiseIQpt;
- int len2000,pt2000;
- int len0800,pt0800;
- double run,inc;
- double i1,q1,fac;
+ long wait;
+ int noiseIQpt,toneIQpt,divpt,rxptr;
+ double i1,q1,fac1,fac2,fac3,fac4;
+ int decimation; // for converting 1536 kHz samples to 48, 192, 384, ....
memcpy(buffer, id, 4);
header_offset = 0;
counter = 0;
- //
- // Produce RX data
- //
noiseIQpt=0;
- //
- // b) some tones in the upper side band (one wave)
- //
- len2000=24 << rate;
- len0800=60 << rate;
-
- inc = 6.283185307179586476925287 / (double) len2000;
- run = 0.0;
- for (i=0; i<len2000; i++) {
- T2000Qtab[i]=cos(run);
- T2000Itab[i]=sin(run);
- run += inc;
- }
- inc = 6.283185307179586476925287 / (double) len0800;
- run = 0.0;
- for (i=0; i<len0800; i++) {
- T0800Qtab[i]=cos(run);
- T0800Itab[i]=sin(run);
- run += inc;
- }
-
- pt2000=0;
- pt0800=0;
-
+ toneIQpt=0;
+ divpt=0;
+ // The rxptr should never "overtake" the txptr, but
+ // it also must not lag behind by too much. We choose
+ // about 50 msec
+ rxptr=txptr-(2500<<rate);
+ if (rxptr < 0) rxptr += RTXLEN;
clock_gettime(CLOCK_MONOTONIC, &delay);
while (1)
#define IM3a 0.60
#define IM3b 0.20
+ // 48 kHz decimation=32
+ // 96 kHz decimation=16
+ // 192 kHz decimation= 8
+ // 384 kHz decimation= 4
+ decimation = 32 >> rate;
for (i = 0; i < 2; ++i)
{
pointer = buffer + i * 516 - i % 2 * 4 + 8;
pointer += 8;
memset(pointer, 0, 504);
+ fac1=rxatt_dbl[0]*0.00001; // Amplitude of 800-Hz-signal to ADC1
+ if (diversity) {
+ fac2=0.0001*rxatt_dbl[0]; // Amplitude of broad "man-made" noise to ADC1
+ fac4=0.0002*rxatt_dbl[1]; // Amplitude of broad "man-made" noise to ADC2 (phase shifted 90 deg.)
+ }
for (j=0; j<n; j++) {
// ADC1: noise + weak tone on RX, feedback sig. on TX (except STEMlab)
if (ptt && (OLDDEVICE != DEVICE_C25)) {
i1=isample[rxptr]*txdrv_dbl;
q1=qsample[rxptr]*txdrv_dbl;
- fac=IM3a+IM3b*(i1*i1+q1*q1);
- adc1isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
- adc1qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+ fac3=IM3a+IM3b*(i1*i1+q1*q1);
+ adc1isample= (txatt_dbl*i1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+ adc1qsample= (txatt_dbl*q1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+ } else if (diversity) {
+ // man made noise only to I samples
+ adc1isample= (noiseItab[noiseIQpt]+toneItab[toneIQpt]*fac1+divtab[divpt]*fac2) * 8388607.0;
+ adc1qsample= (noiseQtab[noiseIQpt]+toneQtab[toneIQpt]*fac1 ) * 8388607.0;
} else {
- adc1isample= noiseItab[noiseIQpt] * 8388607.0; // Noise
- adc1isample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0]; // tone 100 dB below peak
- adc1qsample=noiseQtab[noiseIQpt] * 8388607.0;
- adc1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
+ adc1isample= (noiseItab[noiseIQpt]+toneItab[toneIQpt]*fac1) * 8388607.0;
+ adc1qsample= (noiseQtab[noiseIQpt]+toneQtab[toneIQpt]*fac1) * 8388607.0;
}
- // ADC2: noise + stronger tone on RX, feedback sig. on TX (only STEMlab)
+ // ADC2: noise RX, feedback sig. on TX (only STEMlab)
if (ptt && (OLDDEVICE == DEVICE_C25)) {
i1=isample[rxptr]*txdrv_dbl;
q1=qsample[rxptr]*txdrv_dbl;
- fac=IM3a+IM3b*(i1*i1+q1*q1);
- adc2isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
- adc2qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+ fac3=IM3a+IM3b*(i1*i1+q1*q1);
+ adc2isample= (txatt_dbl*i1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+ adc2qsample= (txatt_dbl*q1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+ } else if (diversity) {
+ // man made noise to Q channel only
+ adc2isample= noiseItab[noiseIQpt] * 8388607.0; // Noise
+ adc2qsample= (noiseQtab[noiseIQpt]+divtab[divpt]*fac4) * 8388607.0;
} else {
adc2isample= noiseItab[noiseIQpt] * 8388607.0; // Noise
- adc2isample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1]; // tone 80 dB below peak
- adc2qsample=noiseQtab[noiseIQpt] * 8388607.0;
- adc2qsample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1];
+ adc2qsample= noiseQtab[noiseIQpt] * 8388607.0;
}
//
- // TX signal with peak=0.4
+ // TX signal with peak=0.407
//
- dacisample= isample[rxptr] * 0.400 * 8388607.0;
- dacqsample= qsample[rxptr] * 0.400 * 8388607.0;
+ dacisample= isample[rxptr] * 0.407 * 8388607.0;
+ dacqsample= qsample[rxptr] * 0.407 * 8388607.0;
for (k=0; k< receivers; k++) {
myisample=0;
}
// Microphone samples: silence
pointer += 2;
- rxptr++; if (rxptr >= RTXLEN) rxptr=0;
- noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV;
- pt2000++; if (pt2000 == len2000) pt2000=0;
- pt0800++; if (pt0800 == len0800) pt0800=0;
+ rxptr++; if (rxptr >= RTXLEN) rxptr=0;
+ noiseIQpt++; if (noiseIQpt >= LENNOISE) noiseIQpt=rand() / NOISEDIV;
+ toneIQpt+=decimation; if (toneIQpt >= LENTONE) toneIQpt=0;
+ divpt+=decimation; if (divpt >= LENDIV) divpt=0;
}
}
//
static int dash_memory = 0;
static int dot_held = 0;
static int dash_held = 0;
-int key_state = 0;
+static int key_state = 0;
static int dot_length = 0;
static int dash_length = 0;
static int kcwl = 0;
static int cwvox = 0;
-int keyer_out = 0;
-
// using clock_nanosleep of librt
extern int clock_nanosleep(clockid_t __clock_id, int __flags,
__const struct timespec *__req,
}
void set_keyer_out(int state) {
- if (keyer_out != state) {
- keyer_out = state;
- if(protocol==NEW_PROTOCOL) schedule_high_priority(9);
- if (state) {
- cw_hold_key(1); // this starts a CW pulse in transmitter.c
- } else {
- cw_hold_key(0); // this stops a CW pulse in transmitter.c
- }
- } else {
- // We should not arrive here in a properly designed keyer
- fprintf(stderr,"SET KEYER OUT: state unchanged: %d", state);
- }
+ switch (protocol) {
+ case ORIGINAL_PROTOCOL:
+ if (state) {
+ cw_hold_key(1); // this starts a CW pulse in transmitter.c
+ } else {
+ cw_hold_key(0); // this stops a CW pulse in transmitter.c
+ }
+ break;
+ case NEW_PROTOCOL:
+ cw_key_state = state;
+ schedule_high_priority();
+ break;
+ }
}
static void* keyer_thread(void *arg) {
EXITLOOP
};
-extern int keyer_out;
-extern int key_state;
-
void keyer_event(int left, int state);
void keyer_update();
void keyer_close();
#ifdef FREEDV
#include "freedv.h"
#endif
-#ifdef LOCALCW
-#include "iambic.h"
-#endif
#include "vox.h"
#include "ext.h"
}
high_priority_buffer_to_radio[4]=running;
if(mode==modeCWU || mode==modeCWL) {
- if(tune) {
+ if(tune || CAT_cw_is_active) {
high_priority_buffer_to_radio[4]|=0x02;
}
#ifdef LOCALCW
if (cw_keyer_internal == 0) {
// set the ptt if we're not in breakin mode and mox is on
if(cw_breakin == 0 && getMox()) high_priority_buffer_to_radio[4]|=0x02;
- high_priority_buffer_to_radio[5]|=(keyer_out) ? 0x01 : 0;
- high_priority_buffer_to_radio[5]|=(key_state==SENDDOT) ? 0x02 : 0;
- high_priority_buffer_to_radio[5]|=(key_state==SENDDASH) ? 0x04 : 0;
}
#endif
+ if (cw_key_state) high_priority_buffer_to_radio[5]|= 0x01;
} else {
if(isTransmitting()) {
high_priority_buffer_to_radio[4]|=0x02;
rxFrequency+=vfo[v].rit;
}
-/*
switch(vfo[v].mode) {
case modeCWU:
rxFrequency-=cw_keyer_sidetone_frequency;
default:
break;
}
-*/
+
phase=(long)((4294967296.0*(double)rxFrequency)/122880000.0);
high_priority_buffer_to_radio[9+(ddc*4)]=phase>>24;
high_priority_buffer_to_radio[10+(ddc*4)]=phase>>16;
}
}
+/*
switch(vfo[active_receiver->id].mode) {
case modeCWU:
txFrequency+=cw_keyer_sidetone_frequency;
default:
break;
}
+*/
phase=(long)((4294967296.0*(double)txFrequency)/122880000.0);
high_priority_buffer_to_radio[1401]=band->OCrx<<1;
}
- // TODO: here protocol is NEVER P1, so delete this
- if((protocol==ORIGINAL_PROTOCOL && device==DEVICE_METIS) ||
-#ifdef USBOZY
- (protocol==ORIGINAL_PROTOCOL && device==DEVICE_OZY) ||
-#endif
- (protocol==NEW_PROTOCOL && device==NEW_DEVICE_ATLAS)) {
- for(r=0;r<receivers;r++) {
- high_priority_buffer_to_radio[1403]|=receiver[i]->preamp;
- }
- }
-
-
long filters=0x00000000;
if(isTransmitting()) {
running=0;
new_protocol_high_priority();
usleep(100000); // 100 ms
+ close (data_socket);
}
void new_protocol_run() {
}
}
- close(data_socket);
return NULL;
}
local_ptt=high_priority_buffer[4]&0x01;
dot=(high_priority_buffer[4]>>1)&0x01;
dash=(high_priority_buffer[4]>>2)&0x01;
- pll_locked=(high_priority_buffer[4]>>3)&0x01;
+ pll_locked=(high_priority_buffer[4]>>4)&0x01;
adc_overload=high_priority_buffer[5]&0x01;
exciter_power=((high_priority_buffer[6]&0xFF)<<8)|(high_priority_buffer[7]&0xFF);
alex_forward_power=((high_priority_buffer[14]&0xFF)<<8)|(high_priority_buffer[15]&0xFF);
alex_reverse_power=((high_priority_buffer[22]&0xFF)<<8)|(high_priority_buffer[23]&0xFF);
supply_volts=((high_priority_buffer[49]&0xFF)<<8)|(high_priority_buffer[50]&0xFF);
+ if (dash || dot) cw_key_hit=1;
+
int tx_vfo=split?VFO_B:VFO_A;
if(vfo[tx_vfo].mode==modeCWL || vfo[tx_vfo].mode==modeCWU) {
local_ptt=local_ptt|dot|dash;
}
- if(previous_ptt!=local_ptt) {
+ if(previous_ptt!=local_ptt && !CAT_cw_is_active) {
g_idle_add(ext_mox_update,(gpointer)(long)(local_ptt));
}
}
}
}
+void new_protocol_flush_iq_samples() {
+//
+// this is called at the end of a TX phase:
+// zero out "rest" of TX IQ buffer and send it
+//
+ while (iqindex < sizeof(iqbuffer)) {
+ iqbuffer[iqindex++]=0;
+ }
+
+ iqbuffer[0]=tx_iq_sequence>>24;
+ iqbuffer[1]=tx_iq_sequence>>16;
+ iqbuffer[2]=tx_iq_sequence>>8;
+ iqbuffer[3]=tx_iq_sequence;
+
+ // send the buffer
+ if(sendto(data_socket,iqbuffer,sizeof(iqbuffer),0,(struct sockaddr*)&iq_addr,iq_addr_length)<0) {
+ fprintf(stderr,"sendto socket failed for iq\n");
+ exit(1);
+ }
+ iqindex=4;
+ tx_iq_sequence++;
+}
+
void new_protocol_iq_samples(int isample,int qsample) {
iqbuffer[iqindex++]=isample>>16;
iqbuffer[iqindex++]=isample>>8;
extern void new_protocol_process_local_mic(unsigned char *buffer,int le);
extern void new_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right_audio_sample);
extern void new_protocol_iq_samples(int isample,int qsample);
+extern void new_protocol_flush_iq_samples();
#endif
#define NUMRECEIVERS 8
-#define LENNOISE 192000
-#define NOISEDIV (RAND_MAX / 96000)
+#define LENNOISE 1536000
+#define NOISEDIV (RAND_MAX / 768000)
-extern double noiseItab[LENNOISE];
-extern double noiseQtab[LENNOISE];
+double noiseItab[LENNOISE];
+double noiseQtab[LENNOISE];
+
+extern int diversity;
+
+#define LENDIV 16000
+double divtab[LENDIV];
+
+#define LENTONE 15360
+extern double toneItab[LENTONE];
+extern double toneQtab[LENTONE];
+
+extern double c1, c2;
#define IM3a 0.60
#define IM3b 0.20
//stat from high-priority packet
static int run=0;
-static int ptt[4];
+static int ptt=0;
static int cwx=0;
static int dot=0;
static int dash=0;
memset(rxfreq, 0, NUMRECEIVERS*sizeof(unsigned long));
memset(alex0, 0, 32*sizeof(int));
memset(alex1, 0, 32*sizeof(int));
- memset(ptt , 0, 4*sizeof(int));
}
}
adc=buffer[4];
fprintf(stderr,"RX: Number of ADCs: %d\n",adc);
}
- for (i=0; i<8; i++) {
+ for (i=0; i<adc; i++) {
rc=(buffer[5] >> i) & 0x01;
if (rc != adcdither[i]) {
adcdither[i]=rc;
fprintf(stderr,"RX: ADC%d dither=%d\n",i,rc);
}
}
- for (i=0; i<8; i++) {
+ for (i=0; i<adc; i++) {
rc=(buffer[6] >> i) & 0x01;
if (rc != adcrandom[i]) {
adcrandom[i]=rc;
if (modified) {
fprintf(stderr,"RX: DDC%d Enable=%d ADC%d Rate=%d SyncMap=%02x\n",
i,ddcenable[i], adcmap[i], rxrate[i], syncddc[i]);
+ rc=0;
+ for (i=0; i<8; i++) {
+ rc += (syncddc[i] >> i) & 0x01;
+ }
+ if (rc > 1) {
+ fprintf(stderr,"WARNING:\n");
+ fprintf(stderr,"WARNING:\n");
+ fprintf(stderr,"WARNING:\n");
+ fprintf(stderr,"WARNING: more than two DDC sync'ed\n");
+ fprintf(stderr,"WARNING: this simulator is not prepeared to handle this case\n");
+ fprintf(stderr,"WARNING: so are most of SDRs around!\n");
+ fprintf(stderr,"WARNING:\n");
+ fprintf(stderr,"WARNING:\n");
+ fprintf(stderr,"WARNING:\n");
+ }
}
}
}
if (pthread_create(&audio_thread_id, NULL, audio_thread, NULL) < 0) {
perror("***** ERROR: Create Audio thread");
}
- } else {
+ } else {
pthread_join(ddc_specific_thread_id, NULL);
pthread_join(duc_specific_thread_id, NULL);
for (i=0; i<NUMRECEIVERS; i++) {
pthread_join(tx_thread_id, NULL);
pthread_join(mic_thread_id, NULL);
pthread_join(audio_thread_id, NULL);
- }
+ }
}
- for (i=0; i<4; i++) {
- rc=(buffer[4] >> (i+1)) & 0x01;
- if (rc != ptt[i]) {
- ptt[i]=rc;
- fprintf(stderr,"HP: PTT%d=%d\n", i, rc);
- }
+ rc=(buffer[4] >> 1) & 0x01;
+ if (rc != ptt) {
+ ptt=rc;
+ fprintf(stderr,"HP: PTT=%d\n", rc);
+ if (ptt == 0) {
+ memset(isample, 0, sizeof(float)*NEWRTXLEN);
+ memset(qsample, 0, sizeof(float)*NEWRTXLEN);
+ }
}
rc=(buffer[5] >> 0) & 0x01;
if (rc != cwx) {
double fac;
int sample;
unsigned char *p;
- int noisept;
+ int noisept,tonept;
int myddc;
long myrate;
int sync,size;
int myadc, syncadc;
int ps=0;
int rxptr;
+ int decimation;
struct timespec delay;
#ifdef __APPLE__
return NULL;
}
- noisept=0;
+ tonept=noisept=0;
clock_gettime(CLOCK_MONOTONIC, &delay);
fprintf(stderr,"RX thread %d, enabled=%d\n", myddc, ddcenable[myddc]);
rxptr=txptr-5000;
if (rxptr < 0) rxptr += NEWRTXLEN;
continue;
}
+ decimation=1536/rxrate[myddc];
myadc=adcmap[myddc];
// for simplicity, we only allow for a single "synchronized" DDC,
// this well covers the PURESIGNAL and DIVERSITY cases
wait=238000000L/rxrate[myddc]; // time for these samples in nano-secs
}
//
- // ADC0: noise (+ distorted TX signal upon TXing)
- // ADC1: noise 20 dB stronger
- // ADC2: original TX signal (ADC1 on HERMES)
+ // ADC0 RX: noise + 800Hz signal at -100 dBm
+ // ADC0 TX: noise + distorted TX signal
+ // ADC1 RX: noise
+ // ADC1 TX: HERMES only: original TX signal
+ // ADC2 : original TX signal
//
- ps=(sync && (rxrate[myadc]==192) && ptt[0] && (syncadc == adc));
+ ps=(sync && (rxrate[myadc]==192) && ptt && (syncadc == adc));
p=buffer;
*p++ =(seqnum >> 24) & 0xFF;
*p++ =(seqnum >> 16) & 0xFF;
// produce noise depending on the ADC
//
i1sample=i0sample=noiseItab[noisept];
- q1sample=q0sample=noiseItab[noisept++];
+ q1sample=q0sample=noiseQtab[noisept++];
if (noisept == LENNOISE) noisept=rand() / NOISEDIV;
- if (myadc == 1) {
- i0sample=i0sample*10.0; // 20 dB more noise on ADC1
- q0sample=q0sample*10.0;
- }
//
// PS: produce sample PAIRS,
// a) distorted TX data (with Drive and Attenuation)
i0sample += irsample*fac;
q0sample += qrsample*fac;
}
+ } else if (myddc == 0) {
+ i0sample += toneItab[tonept] * 0.00001 * rxatt0_dbl;
+ q0sample += toneQtab[tonept] * 0.00001 * rxatt0_dbl;
+ tonept += decimation; if (tonept >= LENTONE) tonept=0;
}
if (sync) {
if (ps) {
// synchronized stream: undistorted TX signal with constant max. amplitude
- i1sample = irsample * 0.329;
- q1sample = qrsample * 0.329;
+ i1sample = irsample * 0.2899;
+ q1sample = qrsample * 0.2899;
}
sample=i0sample * 8388607.0;
*p++=(sample >> 16) & 0xFF;
sample |= (int)((unsigned char)(*p++)&0xFF);
dq = (double) sample / 8388608.0;
//
+// I don't know why (perhaps the CFFIR in the SDR program)
+// but somehow I must multiply the samples to get the correct
+// strength
+//
+ di *= 1.118;
+ dq *= 1.118;
+//
// put TX samples into ring buffer
//
isample[txptr]=di;
p +=6;
- rc=(int) (800.0*sqrt(10*txlevel));
+ rc=(int) ((4095.0/c1)*sqrt(100.0*txlevel*c2));
*p++ = (rc >> 8) & 0xFF;
*p++ = (rc ) & 0xFF;
}
} else {
for (i=0; i<framesPerBuffer; i++) {
- isample=(short) (in[i]*32768.0);
+ isample=(short) (in[i]*32767.0);
*p++ = (isample & 0xFF); // LittleEndian
*p++ = (isample >> 8)& 0xFF;
}
int vox=0;
int CAT_cw_is_active=0;
int cw_key_hit=0;
+int cw_key_state=0;
int n_adc=1;
int diversity_enabled=0;
//fprintf(stderr,"meter_calibration=%f display_calibration=%f\n", meter_calibration, display_calibration);
radioRestoreState();
+//
+// DL1YCF: we send one buffer of TX samples in one shot. For the old
+// protocol, their number is buffer_size, but for the new
+// protocol, the number is 4*buffer_size.
+// Since current hardware has a FIFO of 4096 IQ sample pairs,
+// buffer_size should be limited to 2048 for the old protocol and
+// to 512 for the new protocol.
+//
+ switch (protocol) {
+ case ORIGINAL_PROTOCOL:
+ if (buffer_size > 2048) buffer_size=2048;
+ break;
+ case NEW_PROTOCOL:
+ if (buffer_size > 512) buffer_size=512;
+ break;
+ }
+
radio_change_region(region);
y=0;
extern int vox;
extern int CAT_cw_is_active;
extern int cw_key_hit;
+extern int cw_key_state;
extern int n_adc;
extern int diversity_enabled;
#include "store.h"
#include "ext.h"
#include "rigctl_menu.h"
+#include "new_protocol.h"
+#ifdef LOCALCW
+#include "iambic.h" // declare keyer_update()
+#endif
#include <math.h>
// IP stuff below
static int cw_busy=0;
static int cat_cw_seen=0;
-static long dotlen;
-static long dashlen;
-static int dotsamples;
-static int dashsamples;
+static int dotlen;
+static int dashlen;
+static int dotsamples;
+static int dashsamples;
//
// send_dash() send a "key-down" of a dashlen, followed by a "key-up" of a dotlen
//
void send_dash() {
int TimeToGo;
- for(;;) {
- TimeToGo=cw_key_up+cw_key_down;
- // TimeToGo is invalid if local CW keying has set in
+ if (protocol == ORIGINAL_PROTOCOL) {
+ for(;;) {
+ TimeToGo=cw_key_up+cw_key_down;
+ // TimeToGo is invalid if local CW keying has set in
+ if (cw_key_hit || cw_not_ready) return;
+ if (TimeToGo == 0) break;
+ // sleep until 10 msec before ignition
+ if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
+ // sleep 1 msec
+ usleep(1000L);
+ }
+ // If local CW keying has set in, do not interfere
+ if (cw_key_hit || cw_not_ready) return;
+ cw_key_down = dashsamples;
+ cw_key_up = dotsamples;
+ } else {
if (cw_key_hit || cw_not_ready) return;
- if (TimeToGo == 0) break;
- // sleep until 10 msec before ignition
- if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
- // sleep 1 msec
- usleep(1000L);
+ cw_key_state=1;
+ schedule_high_priority();
+ usleep(dashlen);
+ cw_key_state=0;
+ schedule_high_priority();
+ usleep(dotlen);
}
- // If local CW keying has set in, do not interfere
- if (cw_key_hit || cw_not_ready) return;
- cw_key_down = dashsamples;
- cw_key_up = dotsamples;
}
void send_dot() {
int TimeToGo;
- for(;;) {
- TimeToGo=cw_key_up+cw_key_down;
- // TimeToGo is invalid if local CW keying has set in
+ if (protocol == ORIGINAL_PROTOCOL) {
+ for(;;) {
+ TimeToGo=cw_key_up+cw_key_down;
+ // TimeToGo is invalid if local CW keying has set in
+ if (cw_key_hit || cw_not_ready) return;
+ if (TimeToGo == 0) break;
+ // sleep until 10 msec before ignition
+ if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
+ // sleep 1 msec
+ usleep(1000L);
+ }
+ // If local CW keying has set in, do not interfere
if (cw_key_hit || cw_not_ready) return;
- if (TimeToGo == 0) break;
- // sleep until 10 msec before ignition
- if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
- // sleep 1 msec
- usleep(1000L);
+ cw_key_down = dotsamples;
+ cw_key_up = dotsamples;
+ } else {
+ if (cw_key_hit || cw_not_ready) return;
+ cw_key_state=1;
+ schedule_high_priority();
+ usleep(dotlen);
+ cw_key_state=0;
+ schedule_high_priority();
+ usleep(dotlen);
}
- // If local CW keying has set in, do not interfere
- if (cw_key_hit || cw_not_ready) return;
- cw_key_down = dotsamples;
- cw_key_up = dotsamples;
}
void send_space(int len) {
int TimeToGo;
- for(;;) {
- TimeToGo=cw_key_up+cw_key_down;
- // TimeToGo is invalid if local CW keying has set in
+ if (protocol == ORIGINAL_PROTOCOL) {
+ for(;;) {
+ TimeToGo=cw_key_up+cw_key_down;
+ // TimeToGo is invalid if local CW keying has set in
+ if (cw_key_hit || cw_not_ready) return;
+ if (TimeToGo == 0) break;
+ // sleep until 10 msec before ignition
+ if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
+ // sleep 1 msec
+ usleep(1000L);
+ }
+ // If local CW keying has set in, do not interfere
if (cw_key_hit || cw_not_ready) return;
- if (TimeToGo == 0) break;
- // sleep until 10 msec before ignition
- if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L);
- // sleep 1 msec
- usleep(1000L);
+ cw_key_up = len*dotsamples;
+ } else {
+ if (cw_key_hit || cw_not_ready) return;
+ usleep(len*dotlen);
}
- // If local CW keying has set in, do not interfere
- if (cw_key_hit || cw_not_ready) return;
- cw_key_up = len*dotsamples;
}
void rigctl_send_cw_char(char cw_char) {
#endif
if(key_speed >= 1 && key_speed <= 60) {
cw_keyer_speed=key_speed;
+#ifdef LOCALCW
+ // tell keyer
+ keyer_update();
+#endif
g_idle_add(ext_vfo_update,NULL);
} else {
send_resp(client_sock,"?;");
static int waterfall_samples=0;
static int waterfall_resample=8;
-
-// These three variables are global. Their use is:
+//
+// CW (CAT-CW and LOCALCW) in the "old protocol" is timed by the
+// heart-beat of the mic samples. The communication with rigctl.c
+// and iambic.c is done via some global variables. Their use is:
+//
// cw_key_up/cw_key_down: set number of samples for next key-down/key-up sequence
// Any of these variable will only be set from outside if
// both have value 0.
int cw_key_down = 0;
int cw_not_ready=1;
-// cw_shape_buffer will eventually be integrated into TRANSMITTER
+//
+// In the old protocol, the CW signal is generated within pihpsdr,
+// and the pulses must be shaped. This is done via "cw_shape_buffer".
+// The TX mic samples buffer could possibly be used for this as well.
+//
static double *cw_shape_buffer = NULL;
static int cw_shape = 0;
+//
// cwramp is the function defining the "ramp" of the CW pulse.
// is *must be* an array with 201 entries. The ramp width (200 samples)
-// is hard-coded.
+// is hard-coded. We currently use the impulse response of a
+// Blackman-Harris window.
+//
extern double cwramp[]; // see cwramp.c
extern void cw_audio_write(double sample);
tx->iq_output_buffer=malloc(sizeof(double)*2*tx->output_samples);
tx->samples=0;
tx->pixel_samples=malloc(sizeof(float)*tx->pixels);
- if (cw_shape_buffer) free(cw_shape_buffer);
- cw_shape_buffer=malloc(sizeof(double)*tx->buffer_size);
+ // The CW shape buffer is only used in the old protocol
+ if (protocol == ORIGINAL_PROTOCOL) {
+ if (cw_shape_buffer) free(cw_shape_buffer);
+ cw_shape_buffer=malloc(sizeof(double)*tx->buffer_size);
+ }
fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%p iq_output_buffer=%p pixels=%p\n",tx->mic_input_buffer,tx->iq_output_buffer,tx->pixel_samples);
fprintf(stderr,"create_transmitter: OpenChannel id=%d buffer_size=%d fft_size=%d sample_rate=%d dspRate=%d outputRate=%d\n",
int error;
int cwmode;
int sidetone=0;
+ static int txflag=0;
- // It is important query tx->mode and tune only once, to assure that
- // the two "if (cwmode)" queries give the same result.
+ // It is important to query tx->mode and tune only *once* within this function, to assure that
+ // the two "if (cwmode)" clauses give the same result.
+ // cwmode only valid in the old protocol, in the new protocol we use a different mechanism
cwmode = (tx->mode == modeCWL || tx->mode == modeCWU) && !tune && !tx->twotone;
// also becomes visible on *our* TX spectrum display.
//
dp=tx->iq_output_buffer;
- // These are the I/Q samples that describe our CW signal
- // The only use we make of it is displaying the spectrum.
- for (j = 0; j < tx->output_samples; j++) {
- *dp++ = 0.0;
- *dp++ = cw_shape_buffer[j];
+ switch (protocol) {
+ case ORIGINAL_PROTOCOL:
+ // These are the I/Q samples that describe our CW signal
+ // The only use we make of it is displaying the spectrum.
+ for (j = 0; j < tx->output_samples; j++) {
+ *dp++ = 0.0;
+ *dp++ = cw_shape_buffer[j];
+ }
+ break;
+ case NEW_PROTOCOL:
+ // Produce zero TX signal for "empty" spectrum
+ for (j = 0; j < tx->output_samples; j++) {
+ *dp++ = 0.0;
+ *dp++ = 0.0;
+ }
+ break;
}
} else {
update_vox(tx);
Spectrum0(1, tx->id, 0, 0, tx->iq_output_buffer);
}
- if(isTransmitting()) {
+ if (isTransmitting()) {
if(radio->device==NEW_DEVICE_ATLAS && atlas_penelope) {
//
// For the old protocol where the IQ and audio samples are tied together, we can
// easily generate a synchronous side tone (and use the function
// old_protocol_iq_samples_with_sidetone for this purpose).
+//
+// Note that the CW shape buffer is tied to the mic sample rate (48 kHz).
//
+ if (txflag == 0 && protocol == NEW_PROTOCOL) {
+ //
+ // this is the first time (after a pause) that we send TX samples
+ // so send some "silence" to prevent FIFO underflows
+ //
+ for (j=0; j< 480; j++) {
+ new_protocol_iq_samples(0,0);
+ }
+ }
+ txflag=1;
if (cwmode) {
//
// "pulse shape case":
// Note that tx->iq_output_buffer is not used. Therefore, all the
// SetTXAPostGen functions are not needed for CW!
//
- sidevol= 258.0 * cw_keyer_sidetone_volume; // between 0.0 and 32766.0
- isample=0; // will be constantly zero
- for(j=0;j<tx->output_samples;j++) {
- ramp=cw_shape_buffer[j]; // between 0.0 and 1.0
- qsample=floor(gain*ramp+0.5); // always non-negative, isample is just the pulse envelope
- switch(protocol) {
- case ORIGINAL_PROTOCOL:
- //
- // produce a side tone for internal CW on the HPSDR board
- // since we may use getNextSidetoneSample for local audio, we need
- // an independent instance thereof here. To be nice to the CW
- // operator, the audio is shaped the same way as the RF
- // The extra CPU to calculate the side tone is available here,
- // because we have not called fexchange0 for this buffer.
- //
- sidetone=sidevol * ramp * getNextInternalSideToneSample();
- old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
- break;
- case NEW_PROTOCOL:
- // ToDo: how to produce synchronous side-tone on the HPSDR board?
- new_protocol_iq_samples(isample,qsample);
- break;
+ // In the new protocol, we just put "silence" into the TX IQ buffer
+ //
+ switch (protocol) {
+ case ORIGINAL_PROTOCOL:
+ // Note: tx->output_samples equals tx->buffer_size
+ sidevol= 258.0 * cw_keyer_sidetone_volume; // between 0.0 and 32766.0
+ isample=0; // will be constantly zero
+ for(j=0;j<tx->output_samples;j++) {
+ ramp=cw_shape_buffer[j]; // between 0.0 and 1.0
+ qsample=floor(gain*ramp+0.5); // always non-negative, isample is just the pulse envelope
+ sidetone=sidevol * ramp * getNextInternalSideToneSample();
+ old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
}
+ break;
+ case NEW_PROTOCOL:
+ // Note: tx->output_samples is larger than tx->buffer_size
+ isample=0;
+ qsample=0;
+ for(j=0;j<tx->output_samples;j++) {
+ new_protocol_iq_samples(isample,qsample);
+ }
+ break;
}
} else {
//
}
}
}
+ } else {
+ //
+ // When the buffer has not been sent because MOX has gone,
+ // instead flush the current TX IQ buffer
+ //
+ if (txflag == 1 && protocol == NEW_PROTOCOL) new_protocol_flush_iq_samples();
+ txflag=0;
}
}
// that is, cw_key_down and cw_key_up are much larger than 200.
//
cw_not_ready=0;
- if (cw_key_down > 0 ) {
- if (cw_shape < 200) cw_shape++; // walk up the ramp
- cw_key_down--; // decrement key-up counter
- } else {
- if (cw_key_up >= 0) {
+ switch (protocol) {
+ case ORIGINAL_PROTOCOL:
+ if (cw_key_down > 0 ) {
+ if (cw_shape < 200) cw_shape++; // walk up the ramp
+ cw_key_down--; // decrement key-up counter
+ } else {
+ if (cw_key_up >= 0) {
// dig into this even if cw_key_up is already zero, to ensure
// that we reach the bottom of the ramp for very small pauses
if (cw_shape > 0) cw_shape--; // walk down the ramp
if (cw_key_up > 0) cw_key_up--; // decrement key-down counter
+ }
+ }
+ //
+ // store the ramp value in cw_shape_buffer, but also use it for shaping the "local"
+ // side tone
+ ramp=cwramp[cw_shape];
+ cw_audio_write(0.0078 * getNextSideToneSample() * cw_keyer_sidetone_volume * ramp);
+ cw_shape_buffer[tx->samples]=ramp;
+ break;
+ case NEW_PROTOCOL:
+ //
+ // In the new protocol, we only need to generate a side tone (for local audio) here.
+ // We "climb" the ramp upon "key down", and we "descend" upon "key up"
+ //
+ if (cw_key_state) {
+ // climb up the ramp
+ if (cw_shape < 200) cw_shape++;
+ } else {
+ // walk down the ramp
+ if (cw_shape > 0) cw_shape--;
}
+ ramp=cwramp[cw_shape];
+ cw_audio_write(0.0078 * getNextSideToneSample() * cw_keyer_sidetone_volume * ramp);
+ break;
}
- // store the ramp value in cw_shape_buffer, but also use it for shaping the "local"
- // side tone
- ramp=cwramp[cw_shape];
- cw_audio_write(0.0078 * getNextSideToneSample() * cw_keyer_sidetone_volume * ramp);
- cw_shape_buffer[tx->samples]=ramp;
} else {
//
// If no longer transmitting, or no longer doing CW: reset pulse shaper.
cw_key_up=0;
cw_key_down=0;
cw_shape=0;
- cw_shape_buffer[tx->samples]=0.0;
+ if (protocol == ORIGINAL_PROTOCOL) cw_shape_buffer[tx->samples]=0.0;
}
tx->mic_input_buffer[tx->samples*2]=mic_sample_double;
tx->mic_input_buffer[(tx->samples*2)+1]=0.0; //mic_sample_double;
} else {
tx_set_mode(transmitter,vfo[VFO_A].mode);
}
-
+ // changing from CWL to CWU changes BFO frequency.
+ if (protocol == NEW_PROTOCOL) {
+ schedule_high_priority();
+ }
g_idle_add(ext_vfo_update,NULL);
}