From eb8aed93ff9c7a91dcb08c1b56fc6db1bd3d0293 Mon Sep 17 00:00:00 2001 From: c vw Date: Tue, 23 Jul 2019 19:34:13 +0200 Subject: [PATCH] a) CW and new protocol b) new protocol TX FIFO issues. --- cw_menu.c | 4 + hpsdrsim.c | 299 ++++++++++++++++++++++++++++--------------------- iambic.c | 29 +++-- iambic.h | 3 - new_protocol.c | 57 ++++++---- new_protocol.h | 1 + newhpsdrsim.c | 98 +++++++++++----- portaudio.c | 2 +- radio.c | 18 +++ radio.h | 1 + rigctl.c | 111 +++++++++++------- transmitter.c | 158 ++++++++++++++++++-------- vfo.c | 5 +- 13 files changed, 499 insertions(+), 287 deletions(-) diff --git a/cw_menu.c b/cw_menu.c index 14ec79a..cdd8227 100644 --- a/cw_menu.c +++ b/cw_menu.c @@ -125,6 +125,10 @@ static void cw_keyer_sidetone_frequency_value_changed_cb(GtkWidget *widget, gpoi */ 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) { diff --git a/hpsdrsim.c b/hpsdrsim.c index 59d5e6f..f12ac22 100644 --- a/hpsdrsim.c +++ b/hpsdrsim.c @@ -9,23 +9,30 @@ * * 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 @@ -34,9 +41,9 @@ * 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 #include @@ -91,12 +98,25 @@ static int sock_TCP_Client = -1; 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. @@ -203,7 +223,6 @@ double qsample[RTXLEN]; // shared with newhpsdrsim 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 @@ -237,7 +256,7 @@ int main(int argc, char *argv[]) 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; @@ -260,7 +279,41 @@ int main(int argc, char *argv[]) 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 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 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) { @@ -308,14 +368,6 @@ int main(int argc, char *argv[]) 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; @@ -458,7 +510,7 @@ int main(int argc, char *argv[]) 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! @@ -575,7 +627,11 @@ int main(int argc, char *argv[]) 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); @@ -602,7 +658,7 @@ int main(int argc, char *argv[]) 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); @@ -624,25 +680,21 @@ int main(int argc, char *argv[]) } 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); @@ -656,8 +708,7 @@ int main(int argc, char *argv[]) // 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; @@ -673,8 +724,15 @@ int main(int argc, char *argv[]) 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; @@ -735,7 +793,7 @@ int main(int argc, char *argv[]) buffer[10]=0xFF; buffer[11]=NEWDEVICE; buffer[12]=38; - buffer[13]=103; + buffer[13]=19; buffer[20]=2; buffer[21]=1; buffer[22]=3; @@ -771,7 +829,7 @@ int main(int argc, char *argv[]) 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; @@ -815,7 +873,7 @@ int main(int argc, char *argv[]) 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; @@ -1035,11 +1093,6 @@ void process_ep2(uint8_t *frame) } } -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; @@ -1067,46 +1120,24 @@ void *handler_ep6(void *arg) #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> rate; for (i = 0; i < 2; ++i) { pointer = buffer + i * 516 - i % 2 * 4 + 8; @@ -1140,38 +1176,47 @@ void *handler_ep6(void *arg) 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= 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; } } // diff --git a/iambic.c b/iambic.c index dae4e29..225b6af 100644 --- a/iambic.c +++ b/iambic.c @@ -224,7 +224,7 @@ static int dot_memory = 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; @@ -242,8 +242,6 @@ static sem_t cw_event; 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, @@ -332,18 +330,19 @@ void keyer_event(int left, int state) { } 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) { diff --git a/iambic.h b/iambic.h index 9eaba58..cc83c8f 100644 --- a/iambic.h +++ b/iambic.h @@ -11,9 +11,6 @@ enum { EXITLOOP }; -extern int keyer_out; -extern int key_state; - void keyer_event(int left, int state); void keyer_update(); void keyer_close(); diff --git a/new_protocol.c b/new_protocol.c index 7726ac3..ec1ad8a 100644 --- a/new_protocol.c +++ b/new_protocol.c @@ -59,9 +59,6 @@ #ifdef FREEDV #include "freedv.h" #endif -#ifdef LOCALCW -#include "iambic.h" -#endif #include "vox.h" #include "ext.h" @@ -587,18 +584,16 @@ static void new_protocol_high_priority() { } 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; @@ -615,7 +610,6 @@ static void new_protocol_high_priority() { rxFrequency+=vfo[v].rit; } -/* switch(vfo[v].mode) { case modeCWU: rxFrequency-=cw_keyer_sidetone_frequency; @@ -626,7 +620,7 @@ static void new_protocol_high_priority() { 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; @@ -647,6 +641,7 @@ static void new_protocol_high_priority() { } } +/* switch(vfo[active_receiver->id].mode) { case modeCWU: txFrequency+=cw_keyer_sidetone_frequency; @@ -657,6 +652,7 @@ static void new_protocol_high_priority() { default: break; } +*/ phase=(long)((4294967296.0*(double)txFrequency)/122880000.0); @@ -719,18 +715,6 @@ static void new_protocol_high_priority() { 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;rpreamp; - } - } - - long filters=0x00000000; if(isTransmitting()) { @@ -1139,6 +1123,7 @@ void new_protocol_stop() { running=0; new_protocol_high_priority(); usleep(100000); // 100 ms + close (data_socket); } void new_protocol_run() { @@ -1315,7 +1300,6 @@ fprintf(stderr,"new_protocol_thread: Unknown port %d\n",sourceport); } } - close(data_socket); return NULL; } @@ -1591,18 +1575,20 @@ static void process_high_priority(unsigned char *buffer) { 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)); } } @@ -1691,6 +1677,29 @@ void new_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right } } +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; diff --git a/new_protocol.h b/new_protocol.h index d663c76..0a1bd9c 100644 --- a/new_protocol.h +++ b/new_protocol.h @@ -91,4 +91,5 @@ extern int getTune(); 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 diff --git a/newhpsdrsim.c b/newhpsdrsim.c index c1cc822..132016a 100644 --- a/newhpsdrsim.c +++ b/newhpsdrsim.c @@ -14,11 +14,22 @@ extern void audio_write(int16_t r, int16_t l); #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 @@ -86,7 +97,7 @@ static int txatt=0; //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; @@ -298,7 +309,6 @@ void new_protocol_general_packet(unsigned char *buffer) { 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)); } } @@ -356,14 +366,14 @@ void *ddc_specific_thread(void *data) { adc=buffer[4]; fprintf(stderr,"RX: Number of ADCs: %d\n",adc); } - for (i=0; i<8; i++) { + for (i=0; i> 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> i) & 0x01; if (rc != adcrandom[i]) { adcrandom[i]=rc; @@ -405,6 +415,21 @@ void *ddc_specific_thread(void *data) { 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"); + } } } } @@ -600,7 +625,7 @@ void *highprio_thread(void *data) { 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> (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) { @@ -729,13 +756,14 @@ void *rx_thread(void *data) { 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__ @@ -765,7 +793,7 @@ void *rx_thread(void *data) { 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; @@ -778,6 +806,7 @@ void *rx_thread(void *data) { 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 @@ -800,12 +829,14 @@ void *rx_thread(void *data) { 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; @@ -831,12 +862,8 @@ void *rx_thread(void *data) { // 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) @@ -851,12 +878,16 @@ void *rx_thread(void *data) { 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; @@ -988,6 +1019,13 @@ void *tx_thread(void * data) { 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; @@ -1055,7 +1093,7 @@ void *send_highprio_thread(void *data) { 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; diff --git a/portaudio.c b/portaudio.c index a1ece79..cae5b1d 100644 --- a/portaudio.c +++ b/portaudio.c @@ -196,7 +196,7 @@ int pa_mic_cb(const void *inputBuffer, void *outputBuffer, unsigned long framesP } } else { for (i=0; i> 8)& 0xFF; } diff --git a/radio.c b/radio.c index ce5caad..cdc365d 100644 --- a/radio.c +++ b/radio.c @@ -294,6 +294,7 @@ double vox_hang=250.0; 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; @@ -528,6 +529,23 @@ void start_radio() { //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; diff --git a/radio.h b/radio.h index 8e8c349..6be2ce2 100644 --- a/radio.h +++ b/radio.h @@ -244,6 +244,7 @@ extern double vox_hang; 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; diff --git a/rigctl.c b/rigctl.c index ddb6f87..27f438d 100644 --- a/rigctl.c +++ b/rigctl.c @@ -52,6 +52,10 @@ #include "store.h" #include "ext.h" #include "rigctl_menu.h" +#include "new_protocol.h" +#ifdef LOCALCW +#include "iambic.h" // declare keyer_update() +#endif #include // IP stuff below @@ -297,10 +301,10 @@ static char cw_buf[30]; 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 @@ -314,55 +318,80 @@ static int dashsamples; // 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) { @@ -2206,6 +2235,10 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { #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,"?;"); diff --git a/transmitter.c b/transmitter.c index 3f2a745..663d2f9 100644 --- a/transmitter.c +++ b/transmitter.c @@ -62,8 +62,11 @@ static int filterHigh; 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. @@ -74,12 +77,19 @@ int cw_key_up = 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); @@ -609,8 +619,11 @@ fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%d iq_output_buf 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", @@ -777,9 +790,11 @@ static void full_tx_buffer(TRANSMITTER *tx) { 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; @@ -803,11 +818,22 @@ static void full_tx_buffer(TRANSMITTER *tx) { // 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); @@ -826,7 +852,7 @@ static void full_tx_buffer(TRANSMITTER *tx) { Spectrum0(1, tx->id, 0, 0, tx->iq_output_buffer); } - if(isTransmitting()) { + if (isTransmitting()) { if(radio->device==NEW_DEVICE_ATLAS && atlas_penelope) { // @@ -847,8 +873,20 @@ static void full_tx_buffer(TRANSMITTER *tx) { // 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": @@ -858,29 +896,28 @@ static void full_tx_buffer(TRANSMITTER *tx) { // 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;joutput_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;joutput_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;joutput_samples;j++) { + new_protocol_iq_samples(isample,qsample); + } + break; } } else { // @@ -901,6 +938,13 @@ static void full_tx_buffer(TRANSMITTER *tx) { } } } + } 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; } } @@ -939,22 +983,42 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { // 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. @@ -966,7 +1030,7 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { 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; diff --git a/vfo.c b/vfo.c index 2e907ce..3aac001 100644 --- a/vfo.c +++ b/vfo.c @@ -379,7 +379,10 @@ void vfo_mode_changed(int m) { } 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); } -- 2.45.2