From: c vw Date: Fri, 6 Jul 2018 13:06:06 +0000 (+0200) Subject: Now CAT CW behaves correct when manually hitting the external X-Git-Url: https://git.rkrishnan.org/simplejson/-?a=commitdiff_plain;h=f718b4fbed779329beaabc78fc5c333edbd53f16;p=pihpsdr.git Now CAT CW behaves correct when manually hitting the external CW key during CAT CW transmission, or when manually switching "MOX" during that time. --- diff --git a/Makefile b/Makefile index 169f277..5c0a4fa 100644 --- a/Makefile +++ b/Makefile @@ -405,7 +405,7 @@ ext.o \ error_handler.o $(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) - $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(LIBS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) + $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS) all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES) diff --git a/old_protocol.c b/old_protocol.c index 6ff6c18..4b944f5 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -164,7 +164,6 @@ static int command=1; static GThread *receive_thread_id; static void start_receive_thread(); static gpointer receive_thread(gpointer arg); -// DL1YCF changed buffer to uchar* static void process_ozy_input_buffer(unsigned char *buffer); static void process_bandscope_buffer(char *buffer); void ozy_send_buffer(); @@ -173,10 +172,8 @@ static unsigned char metis_buffer[1032]; static long send_sequence=-1; static int metis_offset=8; -// DL1YCF changed buffer to uchar* static int metis_write(unsigned char ep,unsigned char* buffer,int length); static void metis_start_stop(int command); -// DL1YCF changed buffer to uchar* static void metis_send_buffer(unsigned char* buffer,int length); static void metis_restart(); @@ -380,7 +377,6 @@ static void start_receive_thread() { static gpointer receive_thread(gpointer arg) { struct sockaddr_in addr; - // DL1YCF changed from int to socklen_t socklen_t length; unsigned char buffer[2048]; int bytes_read; @@ -421,7 +417,6 @@ static gpointer receive_thread(gpointer arg) { // get the sequence number sequence=((buffer[4]&0xFF)<<24)+((buffer[5]&0xFF)<<16)+((buffer[6]&0xFF)<<8)+(buffer[7]&0xFF); - // DL1YCF: added check on lost packets if (sequence != last_seq_num+1) { fprintf(stderr,"SEQ ERROR: last %ld, recvd %ld\n", last_seq_num, sequence); } @@ -465,11 +460,9 @@ static gpointer receive_thread(gpointer arg) { break; } } - // DL1YCF added return statement to make compiler happy. return NULL; } -// Dl1YCF changed buffer to uchar* static void process_ozy_input_buffer(unsigned char *buffer) { int i,j; int r; @@ -514,6 +507,7 @@ static void process_ozy_input_buffer(unsigned char *buffer) { dot=(control_in[0]&0x04)==0x04; local_ptt=ptt; + if (dot || dash) external_cw_key_hit=1; if(vfo[tx_vfo].mode==modeCWL || vfo[tx_vfo].mode==modeCWU) { local_ptt=ptt|dot|dash; } @@ -1226,12 +1220,15 @@ void ozy_send_buffer() { } if(mode==modeCWU || mode==modeCWL) { // -// VOX is set in CW if we send -// morse code via CAT. This is -// supported even if we use the -// external keyer for our paddles. +// The default is doing 'external CW', that is, +// CW is entirely done on the HPSDR board. In this +// case, we should not set MOX, the PTT switching on +// the HPSDR board is done by the board itself. // - if(tune || vox) { +// However, if we are doing local CW or tuning, +// we must put the SDR into TX mode. +// + if(isTransmitting() && (tune || local_cw_is_active)) { output_buffer[C0]|=0x01; } } else { @@ -1320,7 +1317,6 @@ static int ozyusb_write(char* buffer,int length) } #endif -// DL1YCF change buffer to uchar* static int metis_write(unsigned char ep,unsigned char* buffer,int length) { int i; @@ -1354,7 +1350,6 @@ static int metis_write(unsigned char ep,unsigned char* buffer,int length) { static void metis_restart() { // reset metis frame - // DL1YCF change == to = in the next line metis_offset=8; // reset current rx @@ -1376,7 +1371,6 @@ static void metis_restart() { ozy_send_buffer(); ozy_send_buffer(); - // DL1YCF: reset for the next commands current_rx=0; command=1; #else @@ -1422,7 +1416,6 @@ static void metis_start_stop(int command) { #endif } -// DL1YCF changedbuffer to uchar * static void metis_send_buffer(unsigned char* buffer,int length) { if(sendto(data_socket,buffer,length,0,(struct sockaddr*)&data_addr,data_addr_length)!=length) { perror("sendto socket failed for metis_send_data\n"); diff --git a/radio.c b/radio.c index a2235ee..0b0c176 100644 --- a/radio.c +++ b/radio.c @@ -1,4 +1,4 @@ -/* Copyrieht (C) +/* Copyright (C) * 2015 - John Melton, G0ORX/N6LYT * * This program is free software; you can redistribute it and/or @@ -288,6 +288,8 @@ double vox_threshold=0.001; double vox_gain=10.0; double vox_hang=250.0; int vox=0; +int local_cw_is_active=0; +int external_cw_key_hit=0; int diversity_enabled=0; double i_rotate[2]={1.0,1.0}; diff --git a/radio.h b/radio.h index d8d6663..c48f1db 100644 --- a/radio.h +++ b/radio.h @@ -244,6 +244,8 @@ extern double vox_threshold; extern double vox_gain; extern double vox_hang; extern int vox; +extern int local_cw_is_active; +extern int external_cw_key_hit; extern int diversity_enabled; extern double i_rotate[2]; diff --git a/rigctl.c b/rigctl.c index 76af632..42a7668 100644 --- a/rigctl.c +++ b/rigctl.c @@ -54,9 +54,6 @@ #include "rigctl_menu.h" #include -extern void vox_trigger(int lead_in); -extern void vox_untrigger(); - // IP stuff below #include #include //inet_addr @@ -320,6 +317,7 @@ extern int cw_key_up, cw_key_down; // void send_dash() { int TimeToGo; + if (external_cw_key_hit) return; for(;;) { TimeToGo=cw_key_up+cw_key_down; if (TimeToGo == 0) break; @@ -334,6 +332,7 @@ void send_dash() { void send_dot() { int TimeToGo; + if (external_cw_key_hit) return; for(;;) { TimeToGo=cw_key_up+cw_key_down; if (TimeToGo == 0) break; @@ -348,6 +347,7 @@ void send_dot() { void send_space(int len) { int TimeToGo; + if (external_cw_key_hit) return; for(;;) { TimeToGo=cw_key_up+cw_key_down; if (TimeToGo == 0) break; @@ -476,34 +476,78 @@ void rigctl_send_cw_char(char cw_char) { // static gpointer rigctl_cw_thread(gpointer data) { - int index; + int i; char c; char local_buf[30]; while (server_running) { // wait for cw_buf become filled with data + // (periodically look every 100 msec) + external_cw_key_hit=0; if (!cw_busy) { usleep(100000L); continue; } + strncpy(local_buf, cw_buf, 30); + cw_busy=0; // mark buffer free again + // these values may have changed dotlen = 1200000L/(long)cw_keyer_speed; dashlen = (dotlen * 3 * cw_keyer_weight) / 50L; dotsamples = 57600 / cw_keyer_speed; dashsamples = (3456 * cw_keyer_weight) / cw_keyer_speed; - strncpy(local_buf, cw_buf, 30); - index=0; - cw_busy=0; // mark buffer free again - // We need a relatively long lead-in time after triggering VOX - // Otherwise the first element is shortened - vox_trigger(100); // lead-in 100 msec, only before the first character is sent. - while(((c=local_buf[index++]) != '\0')) { + local_cw_is_active=1; + if (!mox) { + // activate PTT + g_idle_add(ext_ptt_update ,(gpointer)1); + g_idle_add(ext_cw_key ,(gpointer)1); + // have to wait until it is really there + while (!mox) usleep(50000L); + // some extra time to settle down + usleep(100000L); + } + i=0; + while(((c=local_buf[i++]) != '\0') && !external_cw_key_hit && mox) { rigctl_send_cw_char(c); } - vox_untrigger(); + // + // Either an external CW key has been hit (one connected to the SDR board), + // or MOX has manually been switched. In this case swallow any pending + // or incoming KY messages for about 0.75 sec. We need this long time since + // hamlib waits 0.5 secs after receiving a "KY1" message before trying to + // send the next bunch (do PTT update immediately). + // + if (external_cw_key_hit || !mox) { + local_cw_is_active=0; + g_idle_add(ext_cw_key ,(gpointer)0); + // If an external CW key has been hit, we continue in TX mode + // doing CW manually. Otherwise, switch PTT off. + if (!external_cw_key_hit) { + g_idle_add(ext_ptt_update ,(gpointer)0); + } + for (i=0; i< 75; i++) { + cw_busy=0; // mark buffer free + usleep(10000L); + } + } + // If the next message is pending, continue + if (cw_busy) continue; + local_cw_is_active=0; + // In case of an abort of local CW, this has already been done. + // It is not too harmful to do it again. In case of "normal termination" + // of sending a CAT CW message, we have to do it here. + g_idle_add(ext_cw_key ,(gpointer)0); + if (!external_cw_key_hit) { + g_idle_add(ext_ptt_update ,(gpointer)0); + } } // We arrive here if the rigctl server shuts down. + // This very rarely happens. But we should shut down the + // local CW system gracefully, in case we were in the mid + // of a transmission rigctl_cw_thread_id = NULL; cw_busy=0; + g_idle_add(ext_cw_key ,(gpointer)0); + g_idle_add(ext_ptt_update ,(gpointer)0); return NULL; } @@ -2127,10 +2171,12 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { // changes the WDSP side tone frequency. g_idle_add(ext_cw_setup,NULL); // Initialize for external transmit // We silently ignore buffer overruns. This does not happen with - // hamlib since I fixed it. - if (!cw_busy) { + // hamlib since I fixed it. Note further that the space immediately + // following "KY" is *not* part of the message. + // while cleaning up after hitting external CW key, just skip message + if (!cw_busy && len > 3 && !external_cw_key_hit) { // Copy data to buffer - strncpy(cw_buf, cmd_input+2, 30); + strncpy(cw_buf, cmd_input+3, 30); // Kenwood protocol allows for at most 28 characters, so // this is pure paranoia cw_buf[29]=0; diff --git a/transmitter.c b/transmitter.c index 454ea0c..91f9eb0 100644 --- a/transmitter.c +++ b/transmitter.c @@ -64,6 +64,7 @@ static int waterfall_resample=8; int cw_key_up = 0; int cw_key_down = 0; +// cw_shape_buffer will eventually be integrated into TRANSMITTER static int *cw_shape_buffer = NULL; int cw_shape = 0; @@ -564,7 +565,7 @@ fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%d iq_output_buf 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); + cw_shape_buffer=malloc(sizeof(int)*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", @@ -725,10 +726,10 @@ void tx_set_pre_emphasize(TRANSMITTER *tx,int state) { static void full_tx_buffer(TRANSMITTER *tx) { long isample; long qsample; - double gain,lgain,sidevol; + double gain,fgain,sidevol; int j; int error; - int mode,do_shape; + int mode; int sidetone=0; switch(protocol) { @@ -740,16 +741,10 @@ static void full_tx_buffer(TRANSMITTER *tx) { break; } - // do_shape means that we "shape" the signal with the - // envelope in cw_shape_buf, with values between 0 and 200 - // this is the only way to produce click-free CW signals. mode=vfo[VFO_A].mode; if(split) { mode=vfo[VFO_B].mode; } - do_shape= (mode == modeCWL || mode == modeCWU) && !tune; - if (do_shape) sidevol= 1.29 * cw_keyer_sidetone_volume; // will be multiplied with cw_shape - if (tune) sidevol= 258.0 * cw_keyer_sidetone_volume; update_vox(tx); @@ -776,31 +771,58 @@ static void full_tx_buffer(TRANSMITTER *tx) { } } - if (do_shape) gain=gain*0.005; - for(j=0;joutput_samples;j++) { - lgain=gain; - if (do_shape) lgain=gain*cw_shape_buffer[j]; - double is=tx->iq_output_buffer[j*2]; - double qs=tx->iq_output_buffer[(j*2)+1]; - isample=is>=0.0?(long)floor(is*lgain+0.5):(long)ceil(is*lgain-0.5); - qsample=qs>=0.0?(long)floor(qs*lgain+0.5):(long)ceil(qs*lgain-0.5); - switch(protocol) { - case ORIGINAL_PROTOCOL: - // produce a side tone for internal CW and tune on the HPSDR board - // since we may have used getNextSidetoneSample above we need - // an independent instance thereof here. To be nice to the CW - // operator, the audio is shaped the same way as the RF - if (tune) sidetone=sidevol * getNextInternalSideToneSample(); - if (do_shape) sidetone=sidevol * cw_shape_buffer[j] * getNextInternalSideToneSample(); - old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone); - break; - case NEW_PROTOCOL: - new_protocol_iq_samples(isample,qsample); - break; - } +// Two times essentially the same code: moved the check on "pulse shape" case +// out of the loops for computational efficiency + + if ((mode == modeCWL || mode == modeCWU) && !tune) { + // + // "pulse shape case": + // shape the I/Q samples with the envelope function stored in cw_shape_buffer + // and produce side tone (again with shaped pulses) + // + fgain=gain*0.005; // will be multiplied with cw_shape + sidevol= 1.29 * cw_keyer_sidetone_volume; // will be multiplied with cw_shape + for(j=0;joutput_samples;j++) { + gain=fgain*cw_shape_buffer[j]; + double is=tx->iq_output_buffer[j*2]; + double qs=tx->iq_output_buffer[(j*2)+1]; + isample=is>=0.0?(long)floor(is*gain+0.5):(long)ceil(is*gain-0.5); + qsample=qs>=0.0?(long)floor(qs*gain+0.5):(long)ceil(qs*gain-0.5); + 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 + sidetone=sidevol * cw_shape_buffer[j] * getNextInternalSideToneSample(); + old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone); + break; + case NEW_PROTOCOL: + // ToDo: how to produce side-tone on the HPSDR board? + new_protocol_iq_samples(isample,qsample); + break; + } + } + } else { + // + // Original code without pulse shaping and without side tone + // + for(j=0;joutput_samples;j++) { + double is=tx->iq_output_buffer[j*2]; + double qs=tx->iq_output_buffer[(j*2)+1]; + isample=is>=0.0?(long)floor(is*gain+0.5):(long)ceil(is*gain-0.5); + qsample=qs>=0.0?(long)floor(qs*gain+0.5):(long)ceil(qs*gain-0.5); + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_iq_samples_with_sidetone(isample,qsample,0); + break; + case NEW_PROTOCOL: + new_protocol_iq_samples(isample,qsample); + break; + } + } } } - } void add_mic_sample(TRANSMITTER *tx,short mic_sample) { @@ -815,16 +837,24 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { mode=vfo[0].mode; } - // This is valid if not CW and not tuning - mic_sample_double=(double)mic_sample/32768.0; +// silence TX audio not transmitting, if tuning, or +// when doing CW - if (tune) mic_sample_double=0.0; + if (tune || !isTransmitting() || mode==modeCWL || mode==modeCWU) { + mic_sample_double=0.0; + } else { + mic_sample_double=(double)mic_sample/32768.0; + } + +//This statement takes care that the cw shape buffer is +//automatically wiped if we are not doing local CW + + cw_shape_buffer[tx->samples]=0; if((mode==modeCWL || mode==modeCWU)) { - mic_sample_double=0.0; if (isTransmitting()) { // -// The CW part sets the variables cw_key_up and cw_key_down +// RigCtl CW sets the variables cw_key_up and cw_key_down // to the number of samples for the next down/up sequence. // cw_key_down can be zero, for inserting some space // @@ -844,9 +874,16 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { } } cw_audio_write(0.00003937 * getNextSideToneSample() * cw_keyer_sidetone_volume * cw_shape); + cw_shape_buffer[tx->samples]=cw_shape; + } else { +// +// Have to reset pulse shaper since RigCtl may wait forever +// + cw_key_up=0; + cw_key_down=0; + cw_shape=0; } } - cw_shape_buffer[tx->samples]=cw_shape; tx->mic_input_buffer[tx->samples*2]=mic_sample_double; tx->mic_input_buffer[(tx->samples*2)+1]=0.0; //mic_sample_double; tx->samples++; diff --git a/vox.c b/vox.c index 9058b7d..204c9b5 100644 --- a/vox.c +++ b/vox.c @@ -26,10 +26,8 @@ #include "ext.h" static guint vox_timeout; -static guint trigger_timeout = -1; static double peak=0.0; -static int cwvox=0; static int vox_timeout_cb(gpointer data) { //setVox(0); @@ -38,16 +36,6 @@ static int vox_timeout_cb(gpointer data) { return FALSE; } -static int cwvox_timeout_cb(gpointer data) { - cwvox=0; - trigger_timeout=-1; - g_idle_add(ext_vox_changed,(gpointer)0); - g_idle_add(ext_cw_key ,(gpointer)0); - g_idle_add(ext_vfo_update,NULL); - return FALSE; -} - - double vox_get_peak() { double result=peak; return result; @@ -61,8 +49,6 @@ void update_vox(TRANSMITTER *tx) { double sample; peak=0.0; - if (cwvox) return; // do not set peak and fiddle around with VOX when in local CW mode - for(i=0;ibuffer_size;i++) { sample=tx->mic_input_buffer[i*2]; if(sample<0.0) { @@ -88,29 +74,6 @@ void update_vox(TRANSMITTER *tx) { } } -// vox_trigger activates MOX, vox_enabled does not matter here -// if VOX this is already pending, -void vox_trigger(int lead_in) { - - // delete any pending hang timers - if (trigger_timeout >= 0) g_source_remove(trigger_timeout); - - if (!cwvox) { - cwvox=1; - g_idle_add(ext_vox_changed,(gpointer)1); - g_idle_add(ext_cw_key ,(gpointer)1); - g_idle_add(ext_vfo_update,NULL); - usleep((long)lead_in*1000L); // lead-in time is in ms - } -} - -// vox_untrigger actives the vox hang timer for cwvox -// so an immediately following "KY" CAT command will -// just proceed. -void vox_untrigger() { - trigger_timeout=g_timeout_add((int)vox_hang,cwvox_timeout_cb,NULL); -} - void vox_cancel() { if(vox) { g_source_remove(vox_timeout); diff --git a/vox.h b/vox.h index 34bd48e..e6a1f3f 100644 --- a/vox.h +++ b/vox.h @@ -19,4 +19,5 @@ extern void update_vox(TRANSMITTER *tx); extern void vox_cancel(); +extern void vox_cw_cancel(); extern double vox_get_peak();