From eb8aed93ff9c7a91dcb08c1b56fc6db1bd3d0293 Mon Sep 17 00:00:00 2001
From: c vw <dl1ycf@darc.de>
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 <stdio.h>
 #include <errno.h>
@@ -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<argc; i++) {
+            if (!strncmp(argv[i],"-atlas"  ,      6))  {OLDDEVICE=DEVICE_ATLAS;       NEWDEVICE=NEW_DEVICE_ATLAS;}
+            if (!strncmp(argv[i],"-hermes" ,      7))  {OLDDEVICE=DEVICE_HERMES;      NEWDEVICE=NEW_DEVICE_HERMES;}
+            if (!strncmp(argv[i],"-hermes2" ,     8))  {OLDDEVICE=DEVICE_HERMES2;     NEWDEVICE=NEW_DEVICE_HERMES2;}
+            if (!strncmp(argv[i],"-angelia" ,     8))  {OLDDEVICE=DEVICE_ANGELIA;     NEWDEVICE=NEW_DEVICE_ANGELIA;}
+            if (!strncmp(argv[i],"-orion" ,       6))  {OLDDEVICE=DEVICE_ORION;       NEWDEVICE=NEW_DEVICE_ORION;}
+            if (!strncmp(argv[i],"-orion2" ,      7))  {OLDDEVICE=DEVICE_ORION2;      NEWDEVICE=NEW_DEVICE_ORION2;}
+            if (!strncmp(argv[i],"-hermeslite" , 11))  {OLDDEVICE=DEVICE_HERMES_LITE; NEWDEVICE=NEW_DEVICE_HERMES_LITE;}
+            if (!strncmp(argv[i],"-c25"    ,      4))  {OLDDEVICE=DEVICE_C25;         NEWDEVICE=NEW_DEVICE_HERMES;}
+            if (!strncmp(argv[i],"-diversity",   10))  {diversity=1;}
+        }
+
+        switch (OLDDEVICE) {
+            case   DEVICE_ATLAS:   fprintf(stderr,"DEVICE is ATLASS\n");      c1=3.3; c2=0.090; break;
+            case   DEVICE_HERMES:  fprintf(stderr,"DEVICE is HERMES\n");      c1=3.3; c2=0.095; break;
+            case   DEVICE_HERMES2: fprintf(stderr,"DEVICE is HERMES (2)\n");  c1=3.3; c2=0.095; break;
+            case   DEVICE_ANGELIA: fprintf(stderr,"DEVICE is ANGELIA\n");     c1=3.3; c2=0.095; break;
+            case   DEVICE_ORION:   fprintf(stderr,"DEVICE is ORION\n");       c1=5.0; c2=0.108; break;
+            case   DEVICE_ORION2:  fprintf(stderr,"DEVICE is ORION-II\n");    c1=5.0; c2=0.108; break;
+            case   DEVICE_C25:     fprintf(stderr,"DEVICE is STEMlab/C25\n"); c1=3.3; c2=0.090; break;
+        }
+
+	fprintf(stderr,".... producing random noise\n");
         // Produce some noise
         j=RAND_MAX / 2;
         for (i=0; i<LENNOISE; i++) {
@@ -268,39 +321,46 @@ int main(int argc, char *argv[])
           noiseQtab[i]= ((double) rand() / j - 1.0) * 0.00003;
         }
 
+	fprintf(stderr,".... producing an 800 Hz signal\n");
+	// Produce an 800 Hz tone at 0 dBm
+        run=0.0;
+	for (i=0; i<LENTONE; i++) {
+	  toneQtab[i]=cos(run);
+	  toneItab[i]=sin(run);
+	  run += 0.0032724923474893679567319201909161;
+	}
+
+	if (diversity) {
+	  fprintf(stderr,"DIVERSITY testing activated!\n");
+	  fprintf(stderr,".... producing some man-made noise\n");
+          memset(divtab, 0, LENDIV*sizeof(double));
+          for (j=1; j<=200; j++) {
+            run=0.0;
+            off=0.25*j*j;
+	    inc=j*0.00039269908169872415480783042290994;
+            for (i=0; i< LENDIV; i++) {
+	      divtab[i] += cos(run+off);
+	      run += inc;
+ 	    }
+	  }
+	  // normalize
+	  off=0.0;
+	  for (i=0; i<LENDIV; i++) {
+	    if ( divtab[i] > off) off=divtab[i];
+	    if (-divtab[i] > off) off=-divtab[i];
+	  }
+	  off=1.0/off;
+	  fprintf(stderr,"(normalizing with %f)\n",off);
+	  for (i=0; i<LENDIV; i++) {
+	    divtab[i]=divtab[i]*off;
+	  }
+	}
+	
 	memset (isample, 0, RTXLEN*sizeof(double));
 	memset (qsample, 0, RTXLEN*sizeof(double));
 
 	audio_get_cards();
         audio_open_output();
-/*
- *      Examples for METIS:	Mercury/Penelope boards
- *      Examples for HERMES:	ANAN10, ANAN100
- *      Examples for ANGELIA:   ANAN100D
- *	Examples for ORION:	ANAN200D
- *	Examples for ORION2:	ANAN7000D, ANAN8000D
- */
-
-        if (argc > 1) {
-	    if (!strncmp(argv[1],"-atlas"  ,      6))  {OLDDEVICE=DEVICE_ATLAS;       NEWDEVICE=NEW_DEVICE_ATLAS;}
-	    if (!strncmp(argv[1],"-hermes" ,      7))  {OLDDEVICE=DEVICE_HERMES;      NEWDEVICE=NEW_DEVICE_HERMES;}
-	    if (!strncmp(argv[1],"-hermes2" ,     8))  {OLDDEVICE=DEVICE_HERMES2;     NEWDEVICE=NEW_DEVICE_HERMES2;}
-	    if (!strncmp(argv[1],"-angelia" ,     8))  {OLDDEVICE=DEVICE_ANGELIA;     NEWDEVICE=NEW_DEVICE_ANGELIA;}
-	    if (!strncmp(argv[1],"-orion" ,       6))  {OLDDEVICE=DEVICE_ORION;       NEWDEVICE=NEW_DEVICE_ORION;}
-	    if (!strncmp(argv[1],"-orion2" ,      7))  {OLDDEVICE=DEVICE_ORION2;      NEWDEVICE=NEW_DEVICE_ORION2;}
-	    if (!strncmp(argv[1],"-hermeslite" , 11))  {OLDDEVICE=DEVICE_HERMES_LITE; NEWDEVICE=NEW_DEVICE_HERMES_LITE;}
-	    if (!strncmp(argv[1],"-c25"    ,      4))  {OLDDEVICE=DEVICE_C25;         NEWDEVICE=NEW_DEVICE_HERMES;}
-        }
-	switch (OLDDEVICE) {
-	    case   DEVICE_ATLAS:   fprintf(stderr,"DEVICE is ATLASS\n");      break;
-	    case   DEVICE_HERMES:  fprintf(stderr,"DEVICE is HERMES\n");      break;
-	    case   DEVICE_HERMES2: fprintf(stderr,"DEVICE is HERMES (2)\n");  break;
-	    case   DEVICE_ANGELIA: fprintf(stderr,"DEVICE is ANGELIA\n");     break;
-	    case   DEVICE_ORION:   fprintf(stderr,"DEVICE is ORION\n");       break;
-	    case   DEVICE_ORION2:  fprintf(stderr,"DEVICE is ORION-II\n");    break;
-	    case   DEVICE_C25:     fprintf(stderr,"DEVICE is STEMlab/C25\n"); break;
-	}
-	reply[10]=OLDDEVICE;
 
 	if ((sock_udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 	{
@@ -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<len2000; i++) {
-	  T2000Qtab[i]=cos(run);
-	  T2000Itab[i]=sin(run);
-	  run += inc;
-        }
-	inc = 6.283185307179586476925287 / (double) len0800;
-	run = 0.0;
-        for (i=0; i<len0800; i++) {
-	  T0800Qtab[i]=cos(run);
-	  T0800Itab[i]=sin(run);
-	  run += inc;
-        }
-
-        pt2000=0;
-        pt0800=0;
-	  
+	toneIQpt=0;
+	divpt=0;
+	// The rxptr should never "overtake" the txptr, but
+	// it also must not lag behind by too much. We choose
+	// about 50 msec
+        rxptr=txptr-(2500<<rate);
+        if (rxptr < 0) rxptr += RTXLEN;
         
         clock_gettime(CLOCK_MONOTONIC, &delay);
 	while (1)
@@ -1131,6 +1162,11 @@ void *handler_ep6(void *arg)
 #define IM3a  0.60
 #define IM3b  0.20
 
+		//  48 kHz   decimation=32
+		//  96 kHz   decimation=16
+		// 192 kHz   decimation= 8
+		// 384 kHz   decimation= 4
+		decimation = 32 >> rate;
 		for (i = 0; i < 2; ++i)
 		{
 		    pointer = buffer + i * 516 - i % 2 * 4 + 8;
@@ -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<n; j++) {
 			// ADC1: noise + weak tone on RX, feedback sig. on TX (except STEMlab)
 		        if (ptt && (OLDDEVICE != DEVICE_C25)) {
 			  i1=isample[rxptr]*txdrv_dbl;
 			  q1=qsample[rxptr]*txdrv_dbl;
-			  fac=IM3a+IM3b*(i1*i1+q1*q1);
-			  adc1isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
-			  adc1qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+			  fac3=IM3a+IM3b*(i1*i1+q1*q1);
+			  adc1isample= (txatt_dbl*i1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+			  adc1qsample= (txatt_dbl*q1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+			} else  if (diversity) {
+			  // man made noise only to I samples
+			  adc1isample= (noiseItab[noiseIQpt]+toneItab[toneIQpt]*fac1+divtab[divpt]*fac2) * 8388607.0;
+			  adc1qsample= (noiseQtab[noiseIQpt]+toneQtab[toneIQpt]*fac1                   ) * 8388607.0;
 			} else {
-			  adc1isample= noiseItab[noiseIQpt] * 8388607.0;			// Noise
-			  adc1isample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0];	// tone 100 dB below peak
-			  adc1qsample=noiseQtab[noiseIQpt] * 8388607.0;
-			  adc1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
+			  adc1isample= (noiseItab[noiseIQpt]+toneItab[toneIQpt]*fac1) * 8388607.0;
+			  adc1qsample= (noiseQtab[noiseIQpt]+toneQtab[toneIQpt]*fac1) * 8388607.0;
 			}
-			// ADC2: noise + stronger tone on RX, feedback sig. on TX (only STEMlab)
+			// ADC2: noise RX, feedback sig. on TX (only STEMlab)
 			if (ptt && (OLDDEVICE == DEVICE_C25)) {
 			  i1=isample[rxptr]*txdrv_dbl;
 			  q1=qsample[rxptr]*txdrv_dbl;
-			  fac=IM3a+IM3b*(i1*i1+q1*q1);
-			  adc2isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
-			  adc2qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+			  fac3=IM3a+IM3b*(i1*i1+q1*q1);
+			  adc2isample= (txatt_dbl*i1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+			  adc2qsample= (txatt_dbl*q1*fac3+noiseItab[noiseIQpt]) * 8388607.0;
+			} else if (diversity) {
+			  // man made noise to Q channel only
+			  adc2isample= noiseItab[noiseIQpt] * 8388607.0;			// Noise
+			  adc2qsample= (noiseQtab[noiseIQpt]+divtab[divpt]*fac4) * 8388607.0;
 			} else {
 			  adc2isample= noiseItab[noiseIQpt] * 8388607.0;			// Noise
-			  adc2isample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1];	// tone 80 dB below peak
-			  adc2qsample=noiseQtab[noiseIQpt] * 8388607.0;
-			  adc2qsample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1];
+			  adc2qsample= noiseQtab[noiseIQpt] * 8388607.0;
 			}
 			//
-			// TX signal with peak=0.4
+			// TX signal with peak=0.407
 			//
-			dacisample= isample[rxptr] * 0.400 * 8388607.0;
-			dacqsample= qsample[rxptr] * 0.400 * 8388607.0;
+			dacisample= isample[rxptr] * 0.407 * 8388607.0;
+			dacqsample= qsample[rxptr] * 0.407 * 8388607.0;
 
 			for (k=0; k< receivers; k++) {
 			    myisample=0;
@@ -1214,10 +1259,10 @@ void *handler_ep6(void *arg)
 		        }
 			// Microphone samples: silence
 			pointer += 2;
-			rxptr++;     if (rxptr >= RTXLEN) rxptr=0;
-			noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV;
-			pt2000++;    if (pt2000 == len2000) pt2000=0;
-			pt0800++;    if (pt0800 == len0800) pt0800=0;
+			rxptr++;              if (rxptr >= RTXLEN) rxptr=0;
+			noiseIQpt++;          if (noiseIQpt >= LENNOISE) noiseIQpt=rand() / NOISEDIV;
+			toneIQpt+=decimation; if (toneIQpt >= LENTONE) toneIQpt=0;
+			divpt+=decimation;    if (divpt >= LENDIV) divpt=0;
 		    }
 		}
 		//
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;r<receivers;r++) {
-        high_priority_buffer_to_radio[1403]|=receiver[i]->preamp;
-      }
-    }
-
-
     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<adc; i++) {
        rc=(buffer[5] >> i) & 0x01;
        if (rc != adcdither[i]) {
          adcdither[i]=rc;
 	 fprintf(stderr,"RX: ADC%d dither=%d\n",i,rc);
        }
      }
-     for (i=0; i<8; i++) {
+     for (i=0; i<adc; i++) {
        rc=(buffer[6] >> i) & 0x01;
        if (rc != adcrandom[i]) {
          adcrandom[i]=rc;
@@ -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<NUMRECEIVERS; i++) {
@@ -610,14 +635,16 @@ void *highprio_thread(void *data) {
           pthread_join(tx_thread_id, NULL);
           pthread_join(mic_thread_id, NULL);
           pthread_join(audio_thread_id, NULL);
-        }
+	}
      }
-     for (i=0; i<4; i++) {
-       rc=(buffer[4] >> (i+1)) & 0x01;
-       if (rc != ptt[i]) {
-	ptt[i]=rc;
-	fprintf(stderr,"HP: PTT%d=%d\n", i, rc);
-      }
+     rc=(buffer[4] >> 1) & 0x01;
+     if (rc != ptt) {
+       ptt=rc;
+       fprintf(stderr,"HP: PTT=%d\n", rc);
+       if (ptt == 0) {
+	 memset(isample, 0, sizeof(float)*NEWRTXLEN);
+	 memset(qsample, 0, sizeof(float)*NEWRTXLEN);
+       }
      }
      rc=(buffer[5] >> 0) & 0x01;
      if (rc != cwx) {
@@ -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<framesPerBuffer; i++) {
-      isample=(short) (in[i]*32768.0);
+      isample=(short) (in[i]*32767.0);
       *p++   = (isample & 0xFF);         // LittleEndian
       *p++   = (isample >> 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 <math.h>
 
 // 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;j<tx->output_samples;j++) {
-	    ramp=cw_shape_buffer[j];	    	    // between 0.0 and 1.0
-	    qsample=floor(gain*ramp+0.5);           // always non-negative, isample is just the pulse envelope
-	    switch(protocol) {
-		case ORIGINAL_PROTOCOL:
-		    //
-		    // produce a side tone for internal CW on the HPSDR board
-		    // since we may use getNextSidetoneSample for local audio, we need
-		    // an independent instance thereof here. To be nice to the CW
-		    // operator, the audio is shaped the same way as the RF
-		    // The extra CPU to calculate the side tone is available here,
-		    // because we have not called fexchange0 for this buffer.
-	 	    //
-		    sidetone=sidevol * ramp * getNextInternalSideToneSample();
-		    old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
-		    break;
-		case NEW_PROTOCOL:
-		    // ToDo: how to produce synchronous side-tone on the HPSDR board?
-		    new_protocol_iq_samples(isample,qsample);
-		    break;
+	// In the new protocol, we just put "silence" into the TX IQ buffer
+	//
+	switch (protocol) {
+	  case ORIGINAL_PROTOCOL:
+	    // Note: tx->output_samples equals tx->buffer_size
+            sidevol= 258.0 * cw_keyer_sidetone_volume;  // between 0.0 and 32766.0
+	    isample=0;				    // will be constantly zero
+            for(j=0;j<tx->output_samples;j++) {
+	      ramp=cw_shape_buffer[j];	    	    // between 0.0 and 1.0
+	      qsample=floor(gain*ramp+0.5);         // always non-negative, isample is just the pulse envelope
+	      sidetone=sidevol * ramp * getNextInternalSideToneSample();
+	      old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
 	    }
+	    break;
+	  case NEW_PROTOCOL:
+	    // Note: tx->output_samples is larger than tx->buffer_size
+	    isample=0;
+	    qsample=0;
+	    for(j=0;j<tx->output_samples;j++) {
+	      new_protocol_iq_samples(isample,qsample);
+	    }
+	    break;
 	}
     } else {
 	//
@@ -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