From 7263820f27cb7ab87fba6b81306494e2bd240e05 Mon Sep 17 00:00:00 2001 From: c vw Date: Tue, 21 May 2019 10:38:33 +0200 Subject: [PATCH] VOX corrected, RX/TX switching reworked, streamlining --- Makefile | 2 +- Makefile.mac | 3 +- agc_menu.c | 3 - audio_waterfall.c | 3 - band_menu.c | 3 - bandstack_menu.c | 3 - dsp_menu.c | 1 - equalizer_menu.c | 9 --- error_handler.c | 3 - filter_menu.c | 3 - freqent_menu.c | 1 - hpsdrsim.c | 141 +++++++++++++++++++++++++++++--------- iambic.c | 24 +++++-- main.c | 33 +++++++++ new_discovery.c | 2 - new_protocol.c | 22 ------ new_protocol_programmer.c | 1 - old_discovery.c | 1 - old_protocol.c | 103 ++++++++++++---------------- ps_menu.c | 4 ++ radio.c | 81 +++++++--------------- radio.h | 5 -- rigctl.c | 16 ++--- rx_menu.c | 3 + step_menu.c | 3 - transmitter.c | 60 ++++++++++++++-- vfo.c | 3 - vfo_menu.c | 3 - vox.c | 34 +++++---- waterfall.c | 3 - 30 files changed, 323 insertions(+), 253 deletions(-) diff --git a/Makefile b/Makefile index a6a0f1d..ef76aed 100644 --- a/Makefile +++ b/Makefile @@ -435,7 +435,7 @@ prebuild: clean: -rm -f *.o - -rm -f $(PROGRAM) + -rm -f $(PROGRAM) hpsdrsim install: $(PROGRAM) cp $(PROGRAM) /usr/local/bin diff --git a/Makefile.mac b/Makefile.mac index 26491ea..e3e7f5e 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -436,6 +436,7 @@ clean: -rm -f *.o -rm -f $(PROGRAM) -rm -rf $(PROGRAM).app + -rm -f hpsdrsim install: $(PROGRAM) cp $(PROGRAM) /usr/local/bin @@ -459,7 +460,7 @@ hpsdrsim.o: hpsdrsim.c $(CC) -c -O $(PORTAUDIO_OPTIONS) hpsdrsim.c hpsdrsim: hpsdrsim.o - $(LINK) -o hpsdrsim hpsdrsim.o -lm -lpthread $(AUDIO_LIBS) + $(LINK) -o hpsdrsim hpsdrsim.o $(AUDIO_LIBS) -lm -lpthread ############################################################################# # diff --git a/agc_menu.c b/agc_menu.c index 3c9262d..116ce24 100644 --- a/agc_menu.c +++ b/agc_menu.c @@ -60,9 +60,6 @@ static gboolean agc_select_cb (GtkWidget *widget, gpointer data) { //wdsp_set_agc(CHANNEL_RX0, agc); set_agc(active_receiver, active_receiver->agc); vfo_update(); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/audio_waterfall.c b/audio_waterfall.c index cbf606f..ae89d7c 100644 --- a/audio_waterfall.c +++ b/audio_waterfall.c @@ -78,7 +78,6 @@ waterfall_configure_event_cb (GtkWidget *widget, int height=gtk_widget_get_allocated_height (widget); fprintf(stderr,"audio: waterfall_configure_event: width=%d height=%d\n",width,height); pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); - // DL1YCF changed to uchar * unsigned char *pixels = gdk_pixbuf_get_pixels (pixbuf); memset(pixels, 0, width*height*3); @@ -136,7 +135,6 @@ void audio_waterfall_update() { int i; if(pixbuf) { - // DL1YCF changed to uchar * unsigned char *pixels = gdk_pixbuf_get_pixels (pixbuf); int width=gdk_pixbuf_get_width(pixbuf); @@ -149,7 +147,6 @@ void audio_waterfall_update() { memmove(&pixels[rowstride*(header+1)],&pixels[rowstride*header],(height-(header+1))*rowstride); float sample; - // DL1YCF changed to uchar * unsigned char *p; int average=0; p=&pixels[rowstride*header]; diff --git a/band_menu.c b/band_menu.c index c82a3e8..f1433d1 100644 --- a/band_menu.c +++ b/band_menu.c @@ -64,9 +64,6 @@ gboolean band_select_cb (GtkWidget *widget, gpointer data) { last_band=widget; set_button_text_color(last_band,"orange"); vfo_band_changed(b); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/bandstack_menu.c b/bandstack_menu.c index aa05ff0..4f8dc57 100644 --- a/bandstack_menu.c +++ b/bandstack_menu.c @@ -63,9 +63,6 @@ static gboolean bandstack_select_cb (GtkWidget *widget, gpointer data) { last_bandstack=widget; set_button_text_color(last_bandstack,"orange"); vfo_bandstack_changed(b); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/dsp_menu.c b/dsp_menu.c index 73ea8c7..1e8fcc8 100644 --- a/dsp_menu.c +++ b/dsp_menu.c @@ -70,7 +70,6 @@ static void pre_post_agc_cb(GtkWidget *widget, gpointer data) { } static void nr2_gain_cb(GtkWidget *widget, gpointer data) { -// DL1YCF changed == to = active_receiver->nr2_gain_method=(uintptr_t)data; SetRXAEMNRgainMethod(active_receiver->id, active_receiver->nr2_gain_method); } diff --git a/equalizer_menu.c b/equalizer_menu.c index 325c249..d999d0b 100644 --- a/equalizer_menu.c +++ b/equalizer_menu.c @@ -70,9 +70,6 @@ static gboolean tx_rb_cb (GtkWidget *widget, GdkEventButton *event, gpointer dat gtk_range_set_value(GTK_RANGE(mid_scale),(double)tx_equalizer[2]); gtk_range_set_value(GTK_RANGE(high_scale),(double)tx_equalizer[3]); } - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } @@ -86,9 +83,6 @@ static gboolean rx_rb_cb (GtkWidget *widget, GdkEventButton *event, gpointer dat gtk_range_set_value(GTK_RANGE(mid_scale),(double)rx_equalizer[2]); gtk_range_set_value(GTK_RANGE(high_scale),(double)rx_equalizer[3]); } - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } @@ -100,9 +94,6 @@ static gboolean enable_cb (GtkWidget *widget, GdkEventButton *event, gpointer da enable_rx_equalizer=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); SetRXAEQRun(active_receiver->id, enable_rx_equalizer); } - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/error_handler.c b/error_handler.c index 800b04d..5802a33 100644 --- a/error_handler.c +++ b/error_handler.c @@ -22,9 +22,6 @@ int show_error(void *data) { gtk_widget_show(label); timer=g_timeout_add(5000,timeout_cb,NULL); int result=gtk_dialog_run(GTK_DIALOG(dialog)); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/filter_menu.c b/filter_menu.c index 9d63ab2..3bc8570 100644 --- a/filter_menu.c +++ b/filter_menu.c @@ -88,9 +88,6 @@ static gboolean deviation_select_cb (GtkWidget *widget, gpointer data) { last_filter=widget; set_button_text_color(last_filter,"orange"); vfo_update(); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/freqent_menu.c b/freqent_menu.c index 0370ff7..59d49e3 100644 --- a/freqent_menu.c +++ b/freqent_menu.c @@ -146,7 +146,6 @@ static gboolean freqent_select_cb (GtkWidget *widget, gpointer data) { } } vfo_update(); - // DL1YCF added return statement return FALSE; } diff --git a/hpsdrsim.c b/hpsdrsim.c index 31e6903..6ecf73c 100644 --- a/hpsdrsim.c +++ b/hpsdrsim.c @@ -1,5 +1,7 @@ +//#define MICSAMPLES // define to send microphone samples (triggered by attenuator slider) +//#define TX_ENVELOPE // define to dump TX envelope 1 sec after first PTT /* - * HPSDR simulator, (C) Christoph van Wuellen, April 2019 + * HPSDR simulator, (C) Christoph van Wuellen, April/Mai 2019 * * This program simulates a HPSDR board. * If an SDR program such as phipsdr "connects" with this program, it @@ -11,13 +13,11 @@ * * 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 - * RF3: TX feedback signal with some distortion. This signal is modulated + * RF3: TX feedback signal with some distortion. Signal strength * according to the "TX drive" and "TX ATT" settings. - * RF4: TX signal with a peak value of 0.400 + * RF4: normalized undistorted TX signal with a peak value of 0.400 * - * RF1 and RF2 are attenuated according to the preamp/attenuator settings. - * RF3 respects the "TX drive" and "TX ATT" settings - * RF4 is the TX signal multiplied with 0.4 (ignores TX_DRIVE and TX_ATT). + * RF1 and RF2 respect the Preamp and Attenuator settings * * Depending on the device type, the receivers see different signals * (This is necessary for PURESIGNAL). We chose the association such that @@ -31,6 +31,13 @@ * DEVICE=HERMES: RX3=RF3, RX4=RF4 (also for DEVICE=StemLab) * DEVICE=ORION2: RX4=RF3, RX5=RF5 (also for DEVICE=ANGELIA and ORION) * + * + * Audio sent to the "radio" is played via the first available output channel. + * This works on MacOS (PORTAUDIO) and Linux (ALSA). + * + * 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. */ #include #include @@ -60,7 +67,7 @@ // Forward declarations for the audio functions void audio_get_cards(void); void audio_open_output(); -void audio_write(short, short); +void audio_write(int16_t, int16_t); #ifndef __APPLE__ @@ -109,8 +116,8 @@ static int LineGain = -1; static int MicPTT = -1; static int tip_ring = -1; static int MicBias = -1; -static int ptt=-1; -static int att=-1; +static int ptt=0; +static int AlexAtt=-1; static int TX_class_E = -1; static int OpenCollectorOutputs=-1; static long tx_freq=-1; @@ -138,10 +145,32 @@ static int freq=-1; // floating-point represeners of TX att, RX att, and RX preamp settings -static double txdrv_dbl = 1.0; +static double txdrv_dbl = 0.99; static double txatt_dbl = 1.0; static double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0}; // this reflects both ATT and PREAMP +#ifdef TX_ENVELOPE +// +// This records the size of the first MAXENV TX samples after the first RX-TX transition. +// This can be used to detect whether RX/TX transitions are fast enough not to chop +// the first part of an SSB transmission using VOX, or of the first CW element. +// +#define MAXENV 48000 +static float envelope[MAXENV]; +int envptr=0; +#endif + +#ifdef MICSAMPLES +// +// Raw audio data (48000 16-bit int words) is read from a file and stored. +// As soon as the ALEX attenuators are engaged, these mic samples are +// sent to the SDR program ONCE. +// +static int16_t micsamples[48000]; +int micsamples_ptr=-2; +int micsamples_rate=0; +#endif + static int sock_ep2; static struct sockaddr_in addr_ep6; @@ -203,7 +232,18 @@ int main(int argc, char *argv[]) int udp_retries=0; int bytes_read, bytes_left; uint32_t *code0 = (uint32_t *) buffer; // fast access to code of first buffer - + int fd; + +#ifdef MICSAMPLES + fd=open("micsamples", O_RDONLY); + if (fd >= 0) { + if (read(fd, micsamples, 48000*sizeof(int16_t)) == 48000*sizeof(int16_t)) { + fprintf(stderr,"MIC SAMPLES available\n"); + micsamples_ptr=-1; + } + } +#endif + audio_get_cards(); audio_open_output(); /* @@ -446,6 +486,20 @@ int main(int argc, char *argv[]) sample |= (int) ((signed char) *bp++ & 0xFF); dqsample=(double) sample / 32768.0; +#ifdef TX_ENVELOPE + if (ptt || envptr > 0) { + if (envptr < MAXENV) envelope[envptr++]=(disample*disample+dqsample*dqsample); + if (envptr == MAXENV) { + fd=open("dump.envelope", O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0777); + if (fd >= 0) { + write (fd, envelope, MAXENV*sizeof(float)); + close (fd); + fprintf(stderr,"TX ENV dumped\n"); + } + envptr++; + } + } +#endif switch (rate) { case 0: // RX sample rate = TX sample rate = 48000 isample[txptr ]=disample; @@ -655,7 +709,7 @@ void process_ep2(uint8_t *frame) chk_data(frame[2] & 1, TX_class_E, "TX CLASS-E"); chk_data((frame[2] & 0xfe) >> 1, OpenCollectorOutputs,"OpenCollector"); - chk_data((frame[3] & 0x03) >> 0, att, "ALEX Attenuator"); + chk_data((frame[3] & 0x03) >> 0, AlexAtt, "ALEX Attenuator"); chk_data((frame[3] & 0x04) >> 2, preamp, "ALEX preamp"); chk_data((frame[3] & 0x08) >> 3, LTdither, "LT2208 Dither"); chk_data((frame[3] & 0x10) >> 4, LTrandom, "LT2208 Random"); @@ -669,11 +723,15 @@ void process_ep2(uint8_t *frame) chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq"); if (isc25) { - // Charly25: has two 18-dB preamps that are switched with "preamp" and "dither" - // and two attenuators encoded in Alex-ATT - // Both only applies to RX1! - rxatt_dbl[0]=pow(10.0, -0.05*(12*att-18*LTdither-18*preamp)); - rxatt_dbl[1]=1.0; + // Charly25: has two 18-dB preamps that are switched with "preamp" and "dither" + // and two attenuators encoded in Alex-ATT + // Both only applies to RX1! + rxatt_dbl[0]=pow(10.0, -0.05*(12*AlexAtt-18*LTdither-18*preamp)); + rxatt_dbl[1]=1.0; + } else { + // Assume that it has ALEX attenuators in addition to the Step Attenuators + rxatt_dbl[0]=pow(10.0, -0.05*(10*AlexAtt+rx_att[0])); + rxatt_dbl[1]=1.0; } break; @@ -728,8 +786,8 @@ void process_ep2(uint8_t *frame) chk_data(frame[3] & 0x40,lna6m,"ALEX 6m LNA"); chk_data(frame[3] & 0x80,alexTRdisable,"ALEX T/R disable"); chk_data(frame[4],alex_lpf,"ALEX LPF"); - // reset TX level - txdrv_dbl=(double) txdrive / 255.0; + // reset TX level. Leve a little head-room for noise + txdrv_dbl=(double) txdrive / 256.0; break; case 20: @@ -753,12 +811,16 @@ void process_ep2(uint8_t *frame) chk_data((frame[4] & 0x1F) >> 0, rx_att[0], "RX1 ATT"); chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable"); +#ifdef MICSAMPLES + if (rx_att[0] > 20 && micsamples_ptr == -1) micsamples_ptr = 0; + if (rx_att[0] < 10 && micsamples_ptr == 48000) micsamples_ptr = -1; +#endif if (!isc25) { - // Set RX amplification factors. Assume 20 dB preamps - rxatt_dbl[0]=pow(10.0, -0.05*(rx_att[0]-20*rx_preamp[0])); - rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1])); - rxatt_dbl[2]=pow(10.0, (double) rx_preamp[2]); - rxatt_dbl[3]=pow(10.0, (double) rx_preamp[3]); + // Set RX amplification factors. No switchable preamps available normally. + rxatt_dbl[0]=pow(10.0, -0.05*(10*AlexAtt+rx_att[0])); + rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1])); + rxatt_dbl[2]=1.0; + rxatt_dbl[3]=1.0; } break; @@ -771,8 +833,8 @@ void process_ep2(uint8_t *frame) chk_data(frame[4] & 127, cw_weight,"CW WEIGHT"); chk_data((frame[4] >> 7) & 1, cw_spacing, "CW SPACING"); - // Set RX amplification factors. Assume 20 dB preamps - rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1])); + // Set RX amplification factors. + rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1])); break; case 24: @@ -850,6 +912,7 @@ void *handler_ep6(void *arg) int32_t rf4isample,rf4qsample; int32_t myisample,myqsample; + int16_t ssample; struct timespec delay; #ifdef __APPLE__ @@ -959,13 +1022,13 @@ void *handler_ep6(void *arg) rf2qsample=noiseQtab[noiseIQpt] * 8388607.0; rf2qsample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1]; // - // RF3: TX signal distorted + // RF3: TX signal distorted, with some ADC noise // i1=isample[rxptr]*txdrv_dbl; q1=qsample[rxptr]*txdrv_dbl; fac=IM3a+IM3b*(i1*i1+q1*q1); - rf3isample= txatt_dbl*i1*fac * 8388607.0; - rf3qsample= txatt_dbl*q1*fac * 8388607.0; + rf3isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0; + rf3qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0; // // RF4: TX signal with peak=0.4 // @@ -1028,8 +1091,24 @@ void *handler_ep6(void *arg) *pointer++ = (myqsample >> 8) & 0xFF; *pointer++ = (myqsample >> 0) & 0xFF; } +#ifdef MICSAMPLES + if (micsamples_ptr >= 0 && micsamples_ptr < 48000) { + ssample=micsamples[micsamples_ptr]; + *pointer++ = (ssample >> 8) & 0xFF; + *pointer++ = (ssample ) & 0xFF; + micsamples_rate++; + if (micsamples_rate == 1 << rate) { + micsamples_rate=0; + micsamples_ptr++; + } + } else { + *pointer++ = 0; + *pointer++ = 0; + } +#else // Microphone samples: silence pointer += 2; +#endif rxptr++; if (rxptr >= RTXLEN) rxptr=0; noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV; pt2000++; if (pt2000 == len2000) pt2000=0; @@ -1161,7 +1240,7 @@ void audio_open_output() return; } -void audio_write (short l, short r) +void audio_write (int16_t l, int16_t r) { PaError err; if (playback_handle != NULL) { @@ -1342,7 +1421,7 @@ void audio_open_output() { return; } -void audio_write(short left_sample,short right_sample) { +void audio_write(int16_t left_sample,int16_t right_sample) { snd_pcm_sframes_t delay; int error; long trim; diff --git a/iambic.c b/iambic.c index 70bdde6..56d11b2 100644 --- a/iambic.c +++ b/iambic.c @@ -312,11 +312,6 @@ void keyer_event(int left, int state) { if (state) { // This is for aborting CAT CW messages if a key is hit. cw_key_hit = 1; - // do PTT already here, unless TX mode was activated before - if (running && !cwvox && !mox) { - g_idle_add(ext_mox_update, (gpointer)(long) 1); - } - cwvox=(int) cw_keyer_hang_time; } if (left) { // left paddle hit or released @@ -377,10 +372,29 @@ static void* keyer_thread(void *arg) { sem_wait(&cw_event); #endif + // swallow any cw_events posted during the last "cw hang" time. + if (!kcwl && !kcwr) continue; + + if (!mox) { + g_idle_add(ext_mox_update, (gpointer)(long) 1); + // Wait for mox, that is, wait for WDSP shutting down the RX and + // firing up the TX. This induces a small delay when hitting the key for + // the first time, but excludes that the first dot is swallowed. + // Note: if out-of-band, mox will never come, therefore + // give up after 100 msec. + i=100; + while (!mox && i-- > 0) usleep(1000); + cwvox=(int) cw_keyer_hang_time; + } + key_state = CHECK; clock_gettime(CLOCK_MONOTONIC, &loop_delay); while (key_state != EXITLOOP || cwvox > 0) { + // + // if key_state == EXITLOOP and cwvox == 0, then + // just leave the while-loop without removing MOX + // // re-trigger VOX if *not* busy-spinning if (cwvox > 0 && key_state != EXITLOOP && key_state != CHECK) cwvox=(int) cw_keyer_hang_time; diff --git a/main.c b/main.c index 75a7578..74e8099 100644 --- a/main.c +++ b/main.c @@ -54,6 +54,7 @@ #include "discovery.h" #include "new_protocol.h" #include "old_protocol.h" +#include "frequency.h" // for canTransmit #include "ext.h" struct utsname unameData; @@ -109,6 +110,31 @@ fprintf(stderr,"Creating wisdom file: %s\n", (char *)arg); return NULL; } +// +// handler for key press events. +// SpaceBar presses toggle MOX, everything else downstream +// code to switch mox copied from mox_cb() in toolbar.c, +// but added the correct return values. +// +gboolean keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) { + + if (event->keyval == GDK_KEY_space && radio != NULL) { + if(getTune()==1) { + setTune(0); + } + if(getMox()==1) { + setMox(0); + } else if(canTransmit() || tx_out_of_band) { + setMox(1); + } else { + transmitter_set_out_of_band(transmitter); + } + g_idle_add(ext_vfo_update,NULL); + return TRUE; + } + return FALSE; +} + gboolean main_delete (GtkWidget *widget) { if(radio!=NULL) { #ifdef GPIO @@ -232,6 +258,13 @@ fprintf(stderr,"full screen\n"); g_signal_connect (top_window, "delete-event", G_CALLBACK (main_delete), NULL); //g_signal_connect (top_window,"draw", G_CALLBACK (main_draw_cb), NULL); + // + // We want to use the space-bar as an alternative to go to TX + // + gtk_widget_add_events(top_window, GDK_KEY_PRESS_MASK); + g_signal_connect(top_window, "key_press_event", G_CALLBACK(keypress_cb), NULL); + + //fprintf(stderr,"create fixed container\n"); //fixed=gtk_fixed_new(); //gtk_container_add(GTK_CONTAINER(top_window), fixed); diff --git a/new_discovery.c b/new_discovery.c index 634d99d..3fed57e 100644 --- a/new_discovery.c +++ b/new_discovery.c @@ -185,7 +185,6 @@ void new_discover(struct ifaddrs* iface) { //void* new_discover_receive_thread(void* arg) { gpointer new_discover_receive_thread(gpointer data) { struct sockaddr_in addr; - // DL1YCF change from int to socklen_t socklen_t len; unsigned char buffer[2048]; int bytes_read; @@ -276,6 +275,5 @@ gpointer new_discover_receive_thread(gpointer data) { } fprintf(stderr,"new_discover: exiting new_discover_receive_thread\n"); g_thread_exit(NULL); - // DL1YCF added return statement to make compiler happy. return NULL; } diff --git a/new_protocol.c b/new_protocol.c index 9d855e7..4e839d9 100644 --- a/new_protocol.c +++ b/new_protocol.c @@ -74,13 +74,6 @@ int data_socket=-1; static int running; #ifdef __APPLE__ -// -//DL1YCF: -//Mac OS does not have sem_init for un-named semaphores, -//we have to use named semaphores created with sem_open. -//As a side effect, we consistently have to use sem_t -//pointers instead of sem_t variables -// sem_t *response_sem; #else sem_t response_sem; @@ -200,7 +193,6 @@ static int psk_resample=6; // convert from 48000 to 8000 #define NET_BUFFER_SIZE 2048 // Network buffers static struct sockaddr_in addr; -// DL1YCF next line: changed int to socklen_t static socklen_t length; //static unsigned char buffer[NET_BUFFER_SIZE]; static unsigned char *iq_buffer[MAX_RECEIVERS]; @@ -383,12 +375,6 @@ void new_protocol_init(int pixels) { rc=sem_init(&iq_sem_buffer[ddc], 0, 0); #endif iq_thread_id[ddc] = g_thread_new( "iq thread", iq_thread, (gpointer)(long)i); - //DL1YCF: g_thread new always returns a value, upon failure the program aborts - // so the next four lines have been deactivated. - //if( ! iq_thread_id ) { - // fprintf(stderr,"g_thread_new failed for iq_thread: rx=%d\n",i); - // exit( -1 ); - //} fprintf(stderr, "iq_thread: rx=%d ddc=%d thread=%p\n",i, ddc, iq_thread_id); } @@ -1374,7 +1360,6 @@ static void process_iq_data(RECEIVER *rx) { } rx->iq_sequence++; -// DL1YCF: changed semicolon at end of next line to a plus sign timestamp=((long long)(buffer[4]&0xFF)<<56)+((long long)(buffer[5]&0xFF)<<48)+((long long)(buffer[6]&0xFF)<<40)+((long long)(buffer[7]&0xFF)<<32)+ ((long long)(buffer[8]&0xFF)<<24)+((long long)(buffer[9]&0xFF)<<16)+((long long)(buffer[10]&0xFF)<<8)+(long long)(buffer[11]&0xFF); bitspersample=((buffer[12]&0xFF)<<8)+(buffer[13]&0xFF); @@ -1545,8 +1530,6 @@ static void process_mic_data(int bytes) { sequence=((mic_line_buffer[0]&0xFF)<<24)+((mic_line_buffer[1]&0xFF)<<16)+((mic_line_buffer[2]&0xFF)<<8)+(mic_line_buffer[3]&0xFF); b=4; for(i=0;iid; int tx_vfo=split?VFO_B:VFO_A; + int nreceivers; + +#ifdef PURESIGNAL + // DL1YCF: + // for PureSignal, the number of receivers needed is hard-coded below. + // we need at least 3 (for RX), and up to 5 for Orion2 boards, since + // the TX DAC channel is hard-wired to RX5. + nreceivers=3; + if (device == DEVICE_HERMES) nreceivers=4; + if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=5; +#else +#if defined(RADIOBERRY) || defined(PI_SDR) + nreceivers = receivers; +#else + nreceivers=RECEIVERS; +#endif +#endif + if(buffer[b++]==SYNC && buffer[b++]==SYNC && buffer[b++]==SYNC) { // extract control bytes control_in[0]=buffer[b++]; @@ -623,23 +640,6 @@ static void process_ozy_input_buffer(unsigned char *buffer) { break; } - -#ifdef PURESIGNAL - // DL1YCF: - // for PureSignal, the number of receivers needed is hard-coded below. - // we need at least 3 (for RX), and up to 5 for Orion2 boards, since - // the TX DAC channel is hard-wired to RX5. - nreceivers=3; - if (device == DEVICE_HERMES) nreceivers=4; - if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=5; -#else - #if defined(RADIOBERRY) || defined(PI_SDR) - nreceivers = receivers; - #else - nreceivers=RECEIVERS; - #endif -#endif - int iq_samples=(512-8)/((nreceivers*6)+2); for(i=0;ipuresignal) i=receiver[PS_RX_FEEDBACK]->feedback_antenna; #endif switch(i) { - case 0: // ANT 1 - case 1: // ANT 2 - case 2: // ANT 3 - break; case 3: // Alex: RX2 IN, ANAN: EXT1, ANAN7000: EXT output_buffer[C3]|=0xC0; break; case 4: // Alex: RX1 IN, ANAN: EXT2, ANAN7000: RX BYPASS output_buffer[C3]|=0xA0; break; - case 5: // XVTR - output_buffer[C3]|=0xE0; - break; default: break; } @@ -970,21 +974,9 @@ void ozy_send_buffer() { // TODO - add Alex TX relay, duplex, receivers Mercury board frequency output_buffer[C4]=0x04; // duplex -#ifdef PURESIGNAL - // DL1YCF: see comment on "nreceivers" above. The number is reduced by 1 here - nreceivers=2; - if (device == DEVICE_HERMES) nreceivers=3; - if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=4; -#else - #ifdef RADIOBERRY - nreceivers = receivers-1; - #else - nreceivers=RECEIVERS-1; - #endif -#endif // 0 ... 7 maps on 1 ... 8 receivers - output_buffer[C4]|=nreceivers<<3; + output_buffer[C4]|=(nreceivers-1)<<3; if(isTransmitting()) { switch(transmitter->alex_antenna) { @@ -1030,9 +1022,8 @@ void ozy_send_buffer() { } } else { // - // metis_offset !=8: send "command" C&C packets in round-robin - // using the value of "command" from 1 to 10, - // and "command==2" packets are repeated for each RX + // metis_offset !=8: send the other C&C packets in round-robin + // RX frequency commands are repeated for each RX switch(command) { case 1: // tx frequency output_buffer[C0]=0x02; @@ -1056,19 +1047,6 @@ void ozy_send_buffer() { output_buffer[C4]=txFrequency; break; case 2: // rx frequency -#ifdef PURESIGNAL - // DL1YCF: see comment on "nreceivers" above. - nreceivers=3; - if (device == DEVICE_HERMES) nreceivers=4; - if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=5; -#else - #ifdef RADIOBERRY - nreceivers = receivers; - #else - nreceivers=RECEIVERS; - #endif -#endif - if(current_rxalex_antenna==5) { // XVTR output_buffer[C2]=0x02; // Alex2 XVTR enable } +#ifdef PURESIGNAL + if(transmitter->puresignal) { + output_buffer[C2]|=0x40; // Synchronize RX5 andh TX frequency on transmit + } +#endif output_buffer[C3]=0x00; // Alex2 filters output_buffer[C4]=0x00; // Alex2 filters break; diff --git a/ps_menu.c b/ps_menu.c index 9312459..5008ff0 100644 --- a/ps_menu.c +++ b/ps_menu.c @@ -404,18 +404,22 @@ void ps_menu(GtkWidget *parent) { col++; GtkWidget *ps_ant_auto=gtk_radio_button_new_with_label(NULL,"AUTO"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_auto), + (receiver[PS_RX_FEEDBACK]->feedback_antenna!=3) && (receiver[PS_RX_FEEDBACK]->feedback_antenna!=4)); gtk_widget_show(ps_ant_auto); gtk_grid_attach(GTK_GRID(grid), ps_ant_auto, col, row, 1, 1); g_signal_connect(ps_ant_auto,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 0); col++; GtkWidget *ps_ant_ext1=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT1"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext1), receiver[PS_RX_FEEDBACK]->feedback_antenna==3); gtk_widget_show(ps_ant_ext1); gtk_grid_attach(GTK_GRID(grid), ps_ant_ext1, col, row, 1, 1); g_signal_connect(ps_ant_ext1,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 3); col++; GtkWidget *ps_ant_ext2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT2"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext2), receiver[PS_RX_FEEDBACK]->feedback_antenna==4); gtk_widget_show(ps_ant_ext2); gtk_grid_attach(GTK_GRID(grid), ps_ant_ext2, col, row, 1, 1); g_signal_connect(ps_ant_ext2,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 4); diff --git a/radio.c b/radio.c index 850e271..3c029b4 100644 --- a/radio.c +++ b/radio.c @@ -729,9 +729,10 @@ static void rxtx(int state) { for(i=0;iid,0,1); #else @@ -793,12 +794,13 @@ static void rxtx(int state) { } void setMox(int state) { + vox_cancel(); // remove time-out + if (vox) { // if VOX active, turn it off + rxtx(0); + vox=0; + } if(mox!=state) { - if(vox_enabled && vox) { - vox_cancel(); - } else { - rxtx(state); - } + rxtx(state); mox=state; } } @@ -807,25 +809,25 @@ int getMox() { return mox; } -void setVox(int state) { - if(vox!=state && !tune) { +void vox_changed(int state) { + if(vox!=state && !tune && !mox) { rxtx(state); - vox=state; } - g_idle_add(ext_vfo_update,(gpointer)NULL); -} - -void vox_changed(int state) { - setVox(state); + vox=state; } void setTune(int state) { int i; + // if state==tune, this function is a no-op + if(tune!=state) { - if(vox_enabled && vox) { - vox_cancel(); + vox_cancel(); + if (vox || mox) { + rxtx(0); + vox=0; + mox=0; } if(state) { if(full_tune) { @@ -851,9 +853,10 @@ void setTune(int state) { for(i=0;iid,0,1); #else @@ -925,27 +928,6 @@ int getTune() { return tune; } -// DL1YCF: because of the new CW algorithm, -// this function is no longer used -void radio_cw_setup() { - int mode=vfo[VFO_A].mode;; - if(split) { - mode=vfo[VFO_B].mode; - } - - // DL1YCF: to be "transceive" in CW, our signal - // needs to be spot-on the nominal frequency - SetTXAPostGenToneFreq(transmitter->id,(double) 0.0); - SetTXAPostGenMode(transmitter->id,0); - SetTXAPostGenToneMag(transmitter->id,0.99999); -} - -// DL1YCF: because of the new CW algorithm, -// this function is no longer used -void radio_cw_key(int state) { - SetTXAPostGenRun(transmitter->id,state); -} - int isTransmitting() { return mox | vox | tune; } @@ -1032,21 +1014,6 @@ static int calcLevel(double d) { level=(int)(actual_volts*255.0); -#ifdef __APPLE__ -#ifdef PURESIGNAL -// -// DL1YCF: I do not know exactly why: if the drive level -// is set to zero while PS is active, the program -// reproducably crashes when the drive is set from 1 Watt -// to 0 Watt, possibly a division by zero or the evaluation -// of a logarithm within WDSP. -// QuickAndDirty Fix: use "1" as minimum drive level -// which corresponds to a fraction of a Watt. -// - if (level < 1) level=1; -#endif -#endif - //fprintf(stderr,"calcLevel: %f calib=%f level=%d\n",d, gbb, level); return level; } diff --git a/radio.h b/radio.h index 3f38bfc..8a7656d 100644 --- a/radio.h +++ b/radio.h @@ -261,7 +261,6 @@ extern void setMox(int state); extern int getMox(); extern void setTune(int state); extern int getTune(); -extern void setVox(int state); extern void vox_changed(int state); extern double getDrive(); extern void setDrive(double d); @@ -289,10 +288,6 @@ extern void calculate_display_average(); extern void set_filter_type(int filter_type); extern void set_filter_size(int filter_size); -extern void radio_cw_setup(); - -extern void radio_cw_key(int state); - #ifdef FREEDV extern void set_freedv(int state); #endif diff --git a/rigctl.c b/rigctl.c index 7ec96d9..81b40dc 100644 --- a/rigctl.c +++ b/rigctl.c @@ -79,7 +79,6 @@ int connect_cnt = 0; int rigctlGetFilterLow(); int rigctlGetFilterHigh(); -// DL1YCF changed next to function to void void rigctlSetFilterLow(int val); void rigctlSetFilterHigh(int val); int new_level; @@ -197,7 +196,6 @@ static gpointer set_rigctl_timer (gpointer data) { // Wait throttle time usleep(RIGCTL_TIMER_DELAY); rigctl_timer = 0; - // DL1YCF added return statement to make compiler happy. return NULL; } @@ -552,9 +550,9 @@ static gpointer rigctl_cw_thread(gpointer data) g_idle_add(ext_mox_update ,(gpointer)1); // have to wait until it is really there // Note that if out-of-band, we would wait - // forever here, so allow at most 500 msec - i=10; - while (!mox && (i--) > 0) usleep(50000L); + // forever here, so allow at most 100 msec + i=100; + while (!mox && i-- > 0) usleep(1000L); // still no MOX? --> silently discard CW character and give up if (!mox) { CAT_cw_is_active=0; @@ -2101,16 +2099,16 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { if(agc_resp == 0) { active_receiver->agc = AGC_OFF; - } else if((agc_resp >0 && agc_resp <= 5) || (agc_resp == 84)) { // DL1YCF: added () to improve readability + } else if((agc_resp >0 && agc_resp <= 5) || (agc_resp == 84)) { active_receiver->agc = AGC_FAST; // fprintf(stderr,"GT command FAST\n"); - } else if((agc_resp >6 && agc_resp <= 10) || (agc_resp == 2*84)) { // DL1YCF: added () to improve readability + } else if((agc_resp >6 && agc_resp <= 10) || (agc_resp == 2*84)) { active_receiver->agc = AGC_MEDIUM; // fprintf(stderr,"GT command MED\n"); - } else if((agc_resp >11 && agc_resp <= 15) || (agc_resp == 3*84)) { // DL1YCF: added () to improve readability + } else if((agc_resp >11 && agc_resp <= 15) || (agc_resp == 3*84)) { active_receiver->agc = AGC_SLOW; //fprintf(stderr,"GT command SLOW\n"); - } else if((agc_resp >16 && agc_resp <= 20) || (agc_resp == 4*84)) { // DL1YCF: added () to improve readability + } else if((agc_resp >16 && agc_resp <= 20) || (agc_resp == 4*84)) { active_receiver->agc = AGC_LONG; // fprintf(stderr,"GT command LONG\n"); } diff --git a/rx_menu.c b/rx_menu.c index 2445bdf..54f7e0e 100644 --- a/rx_menu.c +++ b/rx_menu.c @@ -247,6 +247,9 @@ void rx_menu(GtkWidget *parent) { // Instead, we offer these checkboxes in either case and must rely on the user // not playing around with features that are not there. // + // NOTE: Preamps are not present on most current HPSDR models, and ALEX attenuators + // are not present e.g. in ANAN-7000. + // if (filter_board != CHARLY25) { switch(protocol) { case ORIGINAL_PROTOCOL: diff --git a/step_menu.c b/step_menu.c index 233a5b0..e656379 100644 --- a/step_menu.c +++ b/step_menu.c @@ -52,9 +52,6 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d static gboolean step_select_cb (GtkWidget *widget, gpointer data) { step=steps[(uintptr_t)data]; vfo_update(); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/transmitter.c b/transmitter.c index 7c14767..465ec9f 100644 --- a/transmitter.c +++ b/transmitter.c @@ -317,10 +317,61 @@ static gboolean update_display(gpointer data) { // if "MON" button is active (tx->feedback is TRUE), // then obtain spectrum pixels from PS_RX_FEEDBACK, // that is, display the (attenuated) TX signal from the "antenna" + // + // POSSIBLE MISMATCH OF SAMPLE RATES: + // TX sample rate is fixed 48 kHz, but RX sample rate can be + // 2*, 4*, or even 8* larger. In this case, the spectrum shown + // here is squeezed, so we have to extend the pixels. + // So the feedback spectrum is not nice to look at with 192000 Hz + // sample rate (low-res), but at least it is correct. + // For the sake of saving CPU cycles, we do not interpolate. + // + // This correction is applied her for the V1 protocol only, because + // there might be non-integer ratios using the new protocol. + // if(tx->puresignal && tx->feedback) { RECEIVER *rx_feedback=receiver[PS_RX_FEEDBACK]; GetPixels(rx_feedback->id,0,rx_feedback->pixel_samples,&rc); - memcpy(tx->pixel_samples,rx_feedback->pixel_samples,sizeof(float)*tx->pixels); + if (protocol == ORIGINAL_PROTOCOL && (active_receiver->sample_rate != 48000)) { + int ratio = active_receiver->sample_rate / 48000; + int width = tx->pixels / ratio; // number of pixels to copy from the feedback spectrum + int start = (tx->pixels - width) >> 1; // Copy from start ... (end-1) + int end = start + width; + int i; + float *tfp=tx->pixel_samples; + float *rfp=rx_feedback->pixel_samples+start; + switch (ratio) { + case 8: + for (i=start; i < end; i++) { + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp++; + } + break; + case 4: + for (i=start; i < end; i++) { + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp; + *tfp++ = *rfp++; + } + break; + case 2: + for (i=start; i < end; i++) { + *tfp++ = *rfp; + *tfp++ = *rfp++; + } + break; + } + } else { + // TX and feedback sample rates are equal -- just copy + memcpy(tx->pixel_samples,rx_feedback->pixel_samples,sizeof(float)*tx->pixels); + } } else { #endif GetPixels(tx->id,0,tx->pixel_samples,&rc); @@ -890,12 +941,11 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { int i,s; // -// silence TX audio if not transmitting, if tuning, or -// when doing CW. Note: CW side tone added later on by a -// separate mechanism. +// silence TX audio if tuning, or when doing CW. +// (in order not to fire VOX) // - if (tune || !isTransmitting() || mode==modeCWL || mode==modeCWU) { + if (tune || mode==modeCWL || mode==modeCWU) { mic_sample_double=0.0; } else { mic_sample_double=(double)mic_sample/32768.0; diff --git a/vfo.c b/vfo.c index 7d041a7..20ead0c 100644 --- a/vfo.c +++ b/vfo.c @@ -601,9 +601,6 @@ vfo_scroll_event_cb (GtkWidget *widget, } else { vfo_move(-step); } - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/vfo_menu.c b/vfo_menu.c index 0b50e6e..1d77781 100644 --- a/vfo_menu.c +++ b/vfo_menu.c @@ -161,9 +161,6 @@ static gboolean freqent_select_cb (GtkWidget *widget, gpointer data) { } } vfo_update(); - // DL1YCF added return statement to make the compiler happy. - // however I am unsure about the correct return value. - // I would have coded this as a void function. return FALSE; } diff --git a/vox.c b/vox.c index 5b9cbb2..1f9dc80 100644 --- a/vox.c +++ b/vox.c @@ -25,12 +25,16 @@ #include "vfo.h" #include "ext.h" -static guint vox_timeout; +static guint vox_timeout=0; static double peak=0.0; static int vox_timeout_cb(gpointer data) { - //setVox(0); + // + // First set vox_timeout to zero indicating no "hanging" timeout + // then, remove VOX and update display + // + vox_timeout=0; g_idle_add(ext_vox_changed,(gpointer)0); g_idle_add(ext_vfo_update,NULL); return FALSE; @@ -45,7 +49,6 @@ double vox_get_peak() { void update_vox(TRANSMITTER *tx) { // calculate peak microphone input // assumes it is interleaved left and right channel with length samples - int previous_vox=vox; int i; double sample; peak=0.0; @@ -60,25 +63,32 @@ void update_vox(TRANSMITTER *tx) { } if(vox_enabled) { - if(peak>vox_threshold) { - if(previous_vox) { + if(peak > vox_threshold) { + // we use the value of vox_timeout to determine whether + // the time-out is "hanging". We cannot use the value of vox + // since this may be set with a delay, and we MUST NOT miss + // a "hanging" timeout. Note that if a time-out fires, vox_timeout + // is set to zero. + if(vox_timeout) { g_source_remove(vox_timeout); } else { + // + // no hanging time-out, assume that we just fired VOX g_idle_add(ext_vox_changed,(gpointer)1); + g_idle_add(ext_vfo_update,NULL); } + // re-init "vox hang" time vox_timeout=g_timeout_add((int)vox_hang,vox_timeout_cb,NULL); } - if(vox!=previous_vox) { - g_idle_add(ext_vfo_update,NULL); - } + // if peak is not above threshold, do nothing (this shall be done later in the timeout event } } +// +// If no vox time-out is hanging, this function is a no-op +// void vox_cancel() { - if(vox) { + if(vox_timeout) { g_source_remove(vox_timeout); - //setVox(0); - g_idle_add(ext_vox_changed,(gpointer)0); - g_idle_add(ext_vfo_update,NULL); } } diff --git a/waterfall.c b/waterfall.c index bccebcc..7402736 100644 --- a/waterfall.c +++ b/waterfall.c @@ -63,7 +63,6 @@ waterfall_configure_event_cb (GtkWidget *widget, display_height=gtk_widget_get_allocated_height (widget); rx->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, display_width, display_height); - // DL1YCF changed to uchar unsigned char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); memset(pixels, 0, display_width*display_height*3); @@ -123,7 +122,6 @@ void waterfall_update(RECEIVER *rx) { float *samples; if(rx->pixbuf) { - // DL1YCF changed to uchar unsigned char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); int width=gdk_pixbuf_get_width(rx->pixbuf); @@ -171,7 +169,6 @@ void waterfall_update(RECEIVER *rx) { float sample; int average=0; - // DL1YCF changed to uchar unsigned char *p; p=pixels; samples=rx->pixel_samples; -- 2.45.2