]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
a) CW and new protocol b) new protocol TX FIFO issues.
authorc vw <dl1ycf@darc.de>
Tue, 23 Jul 2019 17:34:13 +0000 (19:34 +0200)
committerc vw <dl1ycf@darc.de>
Tue, 23 Jul 2019 17:34:13 +0000 (19:34 +0200)
13 files changed:
cw_menu.c
hpsdrsim.c
iambic.c
iambic.h
new_protocol.c
new_protocol.h
newhpsdrsim.c
portaudio.c
radio.c
radio.h
rigctl.c
transmitter.c
vfo.c

index 14ec79ade528c687062d83b8c6977c8b15df5392..cdd82276a9561fdc9895d3de6f880cce9adeb37e 100644 (file)
--- 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) {
index 59d5e6f74945c6bf3dc6299f565904f96367b8c9..f12ac22bcb47ed10151ae2f0a050a993821ae695 100644 (file)
@@ -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;
                    }
                }
                //
index dae4e29aa1879f60b00467f928d7a4701f693a4d..225b6af3ec10b2cf4219db1cb19e3e68bfa6aaed 100644 (file)
--- 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) {
index 9eaba58cdf947895ec9372e6eed1e9ae7dfd618d..cc83c8fe94069a6f0c06e83d10544830ac266785 100644 (file)
--- 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();
index 7726ac37a53ce00ba199f01f7627ac08cdfa13b9..ec1ad8a8502f1663c9cb4eb7a7549b6ebc8f4a62 100644 (file)
@@ -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;
index d663c76c66e3b215138490e66c544c3a713429fc..0a1bd9c6ab1e9aa91569a531a41a01bd08dc8538 100644 (file)
@@ -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
index c1cc8222e3a1572c60eadca494d0c8d8f105961f..132016a388a9ae8941c06c24bb31635a30fea6c2 100644 (file)
@@ -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;
 
index a1ece79c6d42b53777e116e62479ee83640fed85..cae5b1df8013de3566bf8fff0c555e235201713b 100644 (file)
@@ -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 ce5caad308a51cb619b9e91482a2a30b3cd913c2..cdc365d30c1a077ae0cb97a96cb4ef68a6e187ba 100644 (file)
--- 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 8e8c3492fd15a0b2d6982b6198d39654ed031d10..6be2ce2e0de8eaf808324ee9fd5364228c694f27 100644 (file)
--- 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;
index ddb6f87f64d65959577b3a349881dc926cad60e1..27f438dc7b6cab21b2bff7294a60288435960828 100644 (file)
--- a/rigctl.c
+++ b/rigctl.c
 #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,"?;");
index 3f2a74523d23992668280596e29d9b8d508899fb..663d2f91a5b155bb055380fb7b450026c2dc0402 100644 (file)
@@ -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 2e907ce58466f61c8475f0a82e2c80706792c91b..3aac001f889f102a275f335669a2c560b0b630bb 100644 (file)
--- 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);
 }