From 6a5ef5f328e0eb83658e3b855b8c65a6bc10a030 Mon Sep 17 00:00:00 2001
From: c vw <dl1ycf@darc.de>
Date: Mon, 9 Jul 2018 11:21:27 +0200
Subject: [PATCH] Now behaves correctly if user switches mode during CAT CW
 transmission.

---
 old_protocol.c |  2 +-
 radio.c        |  2 +-
 radio.h        |  2 +-
 rigctl.c       | 94 ++++++++++++++++++++++++++------------------------
 transmitter.c  | 43 +++++++++++++++--------
 5 files changed, 81 insertions(+), 62 deletions(-)

diff --git a/old_protocol.c b/old_protocol.c
index 4b944f5..3c26cae 100644
--- a/old_protocol.c
+++ b/old_protocol.c
@@ -507,7 +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 (dot || dash) cw_key_hit=1;
     if(vfo[tx_vfo].mode==modeCWL || vfo[tx_vfo].mode==modeCWU) {
       local_ptt=ptt|dot|dash;
     }
diff --git a/radio.c b/radio.c
index 0b0c176..854f053 100644
--- a/radio.c
+++ b/radio.c
@@ -289,7 +289,7 @@ 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 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 c48f1db..b455aec 100644
--- a/radio.h
+++ b/radio.h
@@ -245,7 +245,7 @@ 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 cw_key_hit;
 
 extern int diversity_enabled;
 extern double i_rotate[2];
diff --git a/rigctl.c b/rigctl.c
index 42a7668..ec44ba4 100644
--- a/rigctl.c
+++ b/rigctl.c
@@ -303,7 +303,7 @@ static long dashlen;
 static int  dotsamples;
 static int  dashsamples;
 
-extern int cw_key_up, cw_key_down;
+extern int cw_key_up, cw_key_down, cw_not_ready;
 
 //
 // send_dash()         send a "key-down" of a dashlen, followed by a "key-up" of a dotlen
@@ -317,7 +317,7 @@ extern int cw_key_up, cw_key_down;
 //
 void send_dash() {
   int TimeToGo;
-  if (external_cw_key_hit) return;
+  if (cw_key_hit || cw_not_ready) return;
   for(;;) {
     TimeToGo=cw_key_up+cw_key_down;
     if (TimeToGo == 0) break;
@@ -332,7 +332,7 @@ void send_dash() {
 
 void send_dot() {
   int TimeToGo;
-  if (external_cw_key_hit) return;
+  if (cw_key_hit || cw_not_ready) return;
   for(;;) {
     TimeToGo=cw_key_up+cw_key_down;
     if (TimeToGo == 0) break;
@@ -347,7 +347,7 @@ void send_dot() {
 
 void send_space(int len) {
   int TimeToGo;
-  if (external_cw_key_hit) return;
+  if (cw_key_hit || cw_not_ready) return;
   for(;;) {
     TimeToGo=cw_key_up+cw_key_down;
     if (TimeToGo == 0) break;
@@ -483,7 +483,7 @@ static gpointer rigctl_cw_thread(gpointer data)
   while (server_running) {
     // wait for cw_buf become filled with data
     // (periodically look every 100 msec)
-    external_cw_key_hit=0;
+    cw_key_hit=0;
     if (!cw_busy) {
       usleep(100000L);
       continue;
@@ -506,37 +506,38 @@ static gpointer rigctl_cw_thread(gpointer data)
         usleep(100000L);
     }
     i=0;
-    while(((c=local_buf[i++]) != '\0') && !external_cw_key_hit && mox) {
+    while(((c=local_buf[i++]) != '\0') && !cw_key_hit && !cw_not_ready) {
         rigctl_send_cw_char(c);
     }
-    //
-    // 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) {
+    if (cw_key_hit || cw_not_ready) {
+       //
+       // CW transmission has been aborted, either due to manually
+       // removing MOX, changing the mode to non-CW, or because a CW key has been hit.
+       // Do not remove PTT if we abort CAT CW because a CW key has been hit.
        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) {
+       if (!cw_key_hit) {
          g_idle_add(ext_ptt_update ,(gpointer)0);
        }
-       for (i=0; i< 75; i++) {
+       // Stay for 1 sec swallowing incoming
+       // CAT CW commands. We need this long time since
+       // hamlib waits 0.5 secs after receiving a "KY1" message before trying to
+       // send the next bunch
+       for (i=0; i< 50; i++) {
          cw_busy=0;      // mark buffer free
-         usleep(10000L);
+         usleep(20000L);
        }
-    }
-    // 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) {
+    } else {
+      //
+      // CAT CW message has been sent.
+      // If the next message is pending, continue.
+      // Otherwise remove PTT and wait for next CAT
+      // CW command.
+      if (cw_busy) continue;
+      local_cw_is_active=0;
+      g_idle_add(ext_cw_key     ,(gpointer)0);
       g_idle_add(ext_ptt_update ,(gpointer)0);
     }
   }
@@ -2151,34 +2152,37 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
         else if((strcmp(cmd_str,"KY")==0) && (zzid_flag == 0))
 				    { 
 					// DL1YCF:
-					// Hamlib produces errors if we keep begin busy here for
-					// seconds. Therefore we just copy the data to be handled
-					// by a separate thread.
-					// Note that this thread only makes a 0 --> 1 transition for cw_busy,
-					// and the CW thread only makes a 1 --> 0 transition
+					// Hamlib produces timeout errors if we are busy here for
+					// seconds. Therefore we just move the data into a buffer
+					// that is processed by a separate thread.
+					// Note that here we only makes a 0 --> 1 transition for cw_busy,
+					// and the CW thread only makes a 1 --> 0 transition, so we do not
+					// need a mutex and/or atomic updates here.
 					//
-					// Note: for a "KY;" command, we have to return "KY0;" if we can
-					// accept new data (buffer space available) and "KY1;" if we cannot,
+					// Note: The "KY;" command is used to query if we are busy. We return:
+					//  - if we can accept new data (buffer space available) : "KY0;"
+					//  - if buffer is full: "KY1;"
 					//
                                         if (len <= 2) {
 					    if (cw_busy) {
-						send_resp(client_sock,"KY1;");  // can store no more data
+						send_resp(client_sock,"KY1;");
 					    } else {
 						send_resp(client_sock,"KY0;");
 						}
 					} else {
-					    // So we have data. We have to init the CW setup because TUNE
-					    // 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. 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) {
+					    // - We silently ignore buffer overruns. This does not happen  if
+					    //   "busy" is correctly queried.
+					    // - Note further that the space immediately following "KY" is *not*
+					    //   part of the message.
+					    if (!cw_busy && len > 3) {
+						// A CW text will be sent. We have to call cw_setup here because
+						// TUNE changes the WDSP side tone frequency, and in this case we have
+						// to re-adjust it.
+					        g_idle_add(ext_cw_setup,NULL);    // Initialize for external transmit
 						// Copy data to buffer
-						strncpy(cw_buf, cmd_input+3, 30);
-						// Kenwood protocol allows for at most 28 characters, so
-						// this is pure paranoia
+						strncpy(cw_buf, cmd_input+3, 29);
+						// Kenwood protocol allows for at most 24 characters, so
+						// this seems pure paranoia -- but who knows?
 						cw_buf[29]=0;
 						cw_busy=1;
 					    }
diff --git a/transmitter.c b/transmitter.c
index 91f9eb0..3bd504f 100644
--- a/transmitter.c
+++ b/transmitter.c
@@ -62,11 +62,21 @@ static int filterHigh;
 static int waterfall_samples=0;
 static int waterfall_resample=8;
 
+
+// These three variables are global. 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.
+// cw_not_ready:          set to 0 if transmitting in CW mode. This is used to
+//                        abort pending CAT CW messages if MOX or MODE is switched
+//                        manually.
 int cw_key_up = 0;
 int cw_key_down = 0;
+int cw_not_ready=1;
+
 // cw_shape_buffer will eventually be integrated into TRANSMITTER
 static int *cw_shape_buffer = NULL;
-int cw_shape = 0;
+static int cw_shape = 0;
 
 extern void cw_audio_write(double sample);
 
@@ -814,7 +824,7 @@ static void full_tx_buffer(TRANSMITTER *tx) {
 	    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);
+		    old_protocol_iq_samples(isample,qsample);
 		    break;
 		case NEW_PROTOCOL:
 		    new_protocol_iq_samples(isample,qsample);
@@ -837,8 +847,11 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) {
     mode=vfo[0].mode;
   }
 
-// silence TX audio not transmitting, if tuning, or
-// when doing CW
+//
+// silence TX audio if not transmitting, if tuning, or
+// when doing CW. Note: CW side tone added later on by a
+// separate mechanism.
+//
 
   if (tune || !isTransmitting() || mode==modeCWL || mode==modeCWU) {
     mic_sample_double=0.0;
@@ -846,13 +859,10 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) {
     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)) {
-    if (isTransmitting()) {
+//
+// shape CW pulses when doing CW and transmitting, else nullify them
+//
+  if((mode==modeCWL || mode==modeCWU) && isTransmitting()) {
 //
 //	RigCtl CW sets the variables cw_key_up and cw_key_down
 //	to the number of samples for the next down/up sequence.
@@ -862,6 +872,7 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) {
 //	heard way beside our frequency. The envelope goes up
 //	  and down linearly within 200 samples (4.16 msec)
 //
+	cw_not_ready=0;
 	if (cw_key_down > 0 ) {
 	    if (cw_shape < 200) cw_shape++;
 	    cw_key_down--;
@@ -875,14 +886,18 @@ 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 {
+  } else {
 //
-//	Have to reset pulse shaper since RigCtl may wait forever
+//	If no longer transmitting, or no longer doing CW: reset pulse shaper.
+//	This will also swallow any pending CW in rigtl CAT CW and wipe out the
+//      cw_shape buffer very quickly. In order to tell rigctl etc. that CW should be
+//	aborted, we also use the cw_not_ready flag.
 //
+	cw_not_ready=1;
 	cw_key_up=0;
 	cw_key_down=0;
 	cw_shape=0;
-    }
+  	cw_shape_buffer[tx->samples]=0;
   }
   tx->mic_input_buffer[tx->samples*2]=mic_sample_double;
   tx->mic_input_buffer[(tx->samples*2)+1]=0.0; //mic_sample_double;
-- 
2.45.2