]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
HPSDR simulator extended to "New Protocol"
authorc vw <dl1ycf@darc.de>
Wed, 17 Jul 2019 14:29:48 +0000 (16:29 +0200)
committerc vw <dl1ycf@darc.de>
Wed, 17 Jul 2019 14:29:48 +0000 (16:29 +0200)
Makefile
hpsdrsim.c
newhpsdrsim.c [new file with mode: 0644]

index ad0dbd8ed31ce1d73bafe8f1bc8f3e4a26c1871e..652f9bb8a6cfcee65975e33ee0ff4ba0672f2468 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -475,7 +475,10 @@ release: $(PROGRAM)
 #############################################################################
 
 hpsdrsim.o:    hpsdrsim.c
-       $(CC) -c -O hpsdrsim.c
+       $(CC) -c -O -DALSASOUND hpsdrsim.c
 
-hpsdrsim:      hpsdrsim.o
-       $(LINK) -o hpsdrsim hpsdrsim.o $(AUDIO_LIBS) -lm -lpthread
+newhpsdrsim.o: newhpsdrsim.c
+       $(CC) -c -O newhpsdrsim.c
+
+hpsdrsim:      hpsdrsim.o newhpsdrsim.o
+       $(LINK) -o hpsdrsim hpsdrsim.o newhpsdrsim.o -lasound -lm -lpthread
index 0b73aefc6b920055edfc7cace93e90404d19d4ce..358c74a7bf5cf9bd7b13f759b54e0ad56177e71a 100644 (file)
@@ -1,5 +1,3 @@
-//#define MICSAMPLES   // define to send microphone samples (triggered by attenuator slider)
-//#define TX_ENVELOPE  // define to dump TX envelope 1 sec after first PTT
 /*
  * HPSDR simulator, (C) Christoph van Wuellen, April/Mai 2019
  *
@@ -34,7 +32,7 @@
  * and the PURESIGNAL feedback signal is connected to the second ADC.
  *
  * Audio sent to the "radio" is played via the first available output channel.
- * This works on MacOS (PORTAUDIO) and Linux (ALSA).
+ * 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"
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 
+#define NEED_DUMMY_AUDIO 1
+
 #ifdef PORTAUDIO
 #include "portaudio.h"
-#else
+#undef NEED_DUMMY_AUDIO
+#endif
+#ifdef ALSASOUND
 #include <alsa/asoundlib.h>
+#undef NEED_DUMMY_AUDIO
 #endif
 
 // Forward declarations for the audio functions
@@ -82,6 +85,18 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags,
 static int sock_TCP_Server = -1;
 static int sock_TCP_Client = -1;
 
+//
+// Forward declarations for new protocol stuff
+//
+void   new_protocol_general_packet(unsigned char *buffer);
+int    new_protocol_running(void);
+
+#define LENNOISE 192000
+#define NOISEDIV (RAND_MAX / 96000)
+
+double noiseItab[LENNOISE];
+double noiseQtab[LENNOISE];
+
 /*
  * These variables store the state of the SDR.
  * Whenevery they are changed, this is reported.
@@ -150,33 +165,12 @@ static double txdrv_dbl = 0.99;
 static double txatt_dbl = 1.0;
 static double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0};   // this reflects both ATT and PREAMP
 
-#ifdef TX_ENVELOPE
-//
-// This records the size of the first MAXENV TX samples after the first RX-TX transition.
-// This can be used to detect whether RX/TX transitions are fast enough not to chop
-// the first part of an SSB transmission using VOX, or of the first CW element.
-//
-#define MAXENV 48000
-static float envelope[MAXENV];
-int          envptr=0;
-#endif
-
-#ifdef MICSAMPLES
-//
-// Raw audio data (48000 16-bit int words) is read from a file and stored.
-// As soon as the attenuation of RX1 goes above 20dB, these samples are
-// sent to the SDR (one shot), and when the attenuation goes below 10dB, this system
-// is reset, so playing with the ALEX ATT buttons or with the ATT slider you can
-// send mic samples to the SDR application and do some testing.
-//
-static int16_t micsamples[48000];
-int micsamples_ptr=-2;
-int micsamples_rate=0;
-#endif
-
-static int sock_ep2;
-
-static struct sockaddr_in addr_ep6;
+/*
+ * Socket for communicating with the "PC side"
+ */
+static int sock_udp;
+struct sockaddr_in addr_new; // shared by newhpsdrsim.c
+static struct sockaddr_in addr_old;
 
 /*
  * These two variables monitor whether the TX thread is active
@@ -197,23 +191,37 @@ void *handler_ep6(void *arg);
 // and two METIS packets per TCP/UDP packet,
 // and two/four/eight-fold up-sampling if the TX sample
 // rate is 96000/192000/384000
+//
+// In the new protocol, TX samples come in bunches of
+// 240 samples. So NEWRTXLEN is defined as a multiple of
+// 240 not exceeding RTXLEN
+//
 #define RTXLEN 64512
-static double  isample[RTXLEN];
-static double  qsample[RTXLEN];
+#define NEWRTXLEN 64320
+double  isample[RTXLEN];  // shared with newhpsdrsim
+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;
 
-static int ismetis,isorion,ishermes,isc25;
+
+#define DEVICE_ATLAS      0
+#define DEVICE_HERMES     1
+#define DEVICE_HERMES2    2
+#define DEVICE_ANGELIA    3
+#define DEVICE_ORION      4
+#define DEVICE_ORION2     5
+#define DEVICE_HERMESLITE 6
+#define DEVICE_C25        100
+static int DEVICE=DEVICE_HERMES;
 
 int main(int argc, char *argv[])
 {
-       int j, size;
+       int i, j, size;
        struct sched_param param;
        pthread_attr_t attr;
        pthread_t thread;
-        int DEVICE;
 
        uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
 
@@ -221,14 +229,15 @@ int main(int argc, char *argv[])
        uint32_t code;
         int16_t  sample,l,r;
 
-       struct sockaddr_in addr_ep2, addr_from;
+       struct sockaddr_in addr_udp;
        uint8_t buffer[1032];
-       struct iovec iovec;
-       struct msghdr msghdr;
        struct timeval tv;
        struct timespec ts;
        int yes = 1;
         uint8_t *bp;
+        unsigned long checksum;
+        socklen_t lenaddr;
+        struct sockaddr_in addr_from;
 
        uint32_t last_seqnum = 0xffffffff, seqnum;  // sequence number of received packet
 
@@ -236,17 +245,18 @@ int main(int argc, char *argv[])
        int bytes_read, bytes_left;
        uint32_t *code0 = (uint32_t *) buffer;  // fast access to code of first buffer
         int fd;
+        long cnt;
+
+        // Produce some noise
+        j=RAND_MAX / 2;
+        for (i=0; i<LENNOISE; i++) {
+          noiseItab[i]= ((double) rand() / j - 1.0) * 0.00003;
+          noiseQtab[i]= ((double) rand() / j - 1.0) * 0.00003;
+        }
+
+       memset (isample, 0, RTXLEN*sizeof(double));
+       memset (qsample, 0, RTXLEN*sizeof(double));
 
-#ifdef MICSAMPLES
-        fd=open("micsamples", O_RDONLY);
-        if (fd >= 0) {
-         if (read(fd, micsamples, 48000*sizeof(int16_t)) == 48000*sizeof(int16_t)) {
-           fprintf(stderr,"MIC SAMPLES available\n");
-           micsamples_ptr=-1;
-         }
-       }
-#endif
-        
        audio_get_cards();
         audio_open_output();
 /*
@@ -257,25 +267,28 @@ int main(int argc, char *argv[])
  *     Examples for ORION2:    ANAN7000D, ANAN8000D
  */
 
-       DEVICE=1; // default is Hermes
         if (argc > 1) {
-           if (!strncmp(argv[1],"-metis"  ,6))  DEVICE=0;
-           if (!strncmp(argv[1],"-hermes" ,7))  DEVICE=1;
-           if (!strncmp(argv[1],"-orion" , 6))  DEVICE=5;
-           if (!strncmp(argv[1],"-orion2" ,7))  DEVICE=10;   // Anan7000 in old protocol
-           if (!strncmp(argv[1],"-c25"    ,8))  DEVICE=100;  // the same as hermes
+           if (!strncmp(argv[1],"-atlas"  ,      6))  DEVICE=DEVICE_ATLAS;
+           if (!strncmp(argv[1],"-hermes" ,      7))  DEVICE=DEVICE_HERMES;
+           if (!strncmp(argv[1],"-hermes2" ,     8))  DEVICE=DEVICE_HERMES2;
+           if (!strncmp(argv[1],"-angelia" ,     8))  DEVICE=DEVICE_ANGELIA;
+           if (!strncmp(argv[1],"-orion" ,       6))  DEVICE=DEVICE_ORION;
+           if (!strncmp(argv[1],"-orion2" ,      7))  DEVICE=DEVICE_ORION2;
+           if (!strncmp(argv[1],"-hermeslite" , 11))  DEVICE=DEVICE_ORION2;
+           if (!strncmp(argv[1],"-c25"    ,      4))  DEVICE=DEVICE_C25;
         }
-       ismetis=ishermes=isorion=isc25=0;
        switch (DEVICE) {
-           case   0: fprintf(stderr,"DEVICE is METIS\n");   ismetis=1;  break;
-           case   1: fprintf(stderr,"DEVICE is HERMES\n");  ishermes=1; break;
-           case   5: fprintf(stderr,"DEVICE is ORION\n");   isorion=1;  break;
-           case  10: fprintf(stderr,"DEVICE is ORION2\n");  isorion=1;  break;
-           case 100: fprintf(stderr,"DEVICE is StemLab\n"); ishermes=1;  isc25=1; break;
+           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]=DEVICE;
 
-       if ((sock_ep2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+       if ((sock_udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        {
                perror("socket");
                return EXIT_FAILURE;
@@ -289,18 +302,18 @@ int main(int argc, char *argv[])
        reply[7]=0xEE;
        reply[8]=0xFF;
 
-       setsockopt(sock_ep2, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+       setsockopt(sock_udp, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
 
        tv.tv_sec = 0;
        tv.tv_usec = 1000;
-       setsockopt(sock_ep2, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+       setsockopt(sock_udp, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
 
-       memset(&addr_ep2, 0, sizeof(addr_ep2));
-       addr_ep2.sin_family = AF_INET;
-       addr_ep2.sin_addr.s_addr = htonl(INADDR_ANY);
-       addr_ep2.sin_port = htons(1024);
+       memset(&addr_udp, 0, sizeof(addr_udp));
+       addr_udp.sin_family = AF_INET;
+       addr_udp.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr_udp.sin_port = htons(1024);
 
-       if (bind(sock_ep2, (struct sockaddr *)&addr_ep2, sizeof(addr_ep2)) < 0)
+       if (bind(sock_udp, (struct sockaddr *)&addr_udp, sizeof(addr_udp)) < 0)
        {
                perror("bind");
                return EXIT_FAILURE;
@@ -325,7 +338,7 @@ int main(int argc, char *argv[])
        tv.tv_usec = 1000;
        setsockopt(sock_TCP_Server, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
 
-       if (bind(sock_TCP_Server, (struct sockaddr *)&addr_ep2, sizeof(addr_ep2)) < 0)
+       if (bind(sock_TCP_Server, (struct sockaddr *)&addr_udp, sizeof(addr_udp)) < 0)
        {
                perror("bind tcp");
                return EXIT_FAILURE;
@@ -339,16 +352,7 @@ int main(int argc, char *argv[])
 
        while (1)
        {
-               memset(&iovec, 0, sizeof(iovec));
-               memset(&msghdr, 0, sizeof(msghdr));
-
                memcpy(buffer, id, 4);
-               iovec.iov_base = buffer;
-               iovec.iov_len = 1032;
-               msghdr.msg_iov = &iovec;
-               msghdr.msg_iovlen = 1;
-               msghdr.msg_name = &addr_from;
-               msghdr.msg_namelen = sizeof(addr_from);
 
                ts.tv_sec = 0;
                ts.tv_nsec = 1000000;
@@ -405,7 +409,8 @@ int main(int argc, char *argv[])
                }
                else
                {
-                       bytes_read = recvmsg(sock_ep2, &msghdr, 0);
+                       lenaddr=sizeof(addr_from);
+                       bytes_read = recvfrom(sock_udp, buffer, 1032, 0, (struct sockaddr *)&addr_from, &lenaddr);
                        if (bytes_read > 0)
                        {
                                udp_retries=0;
@@ -439,8 +444,8 @@ int main(int argc, char *argv[])
 
                        switch (code)
                        {
-                               // PC to Red Pitaya transmission via process_ep2
-                               case 0x0201feef:
+                           // PC to Red Pitaya transmission via process_ep2
+                           case 0x0201feef:
 
                                // processing an invalid packet is too dangerous -- skip it!
                                if (bytes_read != 1032)
@@ -472,7 +477,9 @@ int main(int argc, char *argv[])
                                  // Note that this interpolation causes weak "sidebands" at 48/96/... kHz distance (the
                                  // strongest ones at 48 kHz).
                                  double disample,dqsample,idelta,qdelta;
+                                 double sum;
                                   bp=buffer+16;  // skip 8 header and 8 SYNC/C&C bytes
+                                 sum=0.0;
                                   for (j=0; j<126; j++) {
                                        // write audio samples
                                        r  = (int)((signed char) *bp++)<<8;
@@ -482,25 +489,12 @@ int main(int argc, char *argv[])
                                         audio_write(r,l);
                                        sample  = (int)((signed char) *bp++)<<8;
                                        sample |= (int) ((signed char) *bp++ & 0xFF);
-                                       disample=(double) sample / 32768.0;
+                                       disample=(double) sample * 0.000030517578125;  // division by 32768
                                        sample  = (int)((signed char) *bp++)<<8;
                                        sample |= (int) ((signed char) *bp++ & 0xFF);
-                                       dqsample=(double) sample / 32768.0;
-
-#ifdef TX_ENVELOPE
-                                       if (ptt || envptr > 0) {
-                                         if (envptr < MAXENV) envelope[envptr++]=(disample*disample+dqsample*dqsample);
-                                         if (envptr == MAXENV) {
-                                           fd=open("dump.envelope", O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0777);
-                                           if (fd >= 0) {
-                                             write (fd, envelope, MAXENV*sizeof(float));
-                                             close (fd);
-                                             fprintf(stderr,"TX ENV dumped\n");
-                                           }
-                                           envptr++;
-                                         }
-                                       }
-#endif
+                                       dqsample=(double) sample * 0.000030517578125;
+                                       sum += (disample*disample+dqsample*dqsample);
+
                                        switch (rate) {
                                            case 0:  // RX sample rate = TX sample rate = 48000
                                                isample[txptr  ]=disample;
@@ -551,13 +545,14 @@ int main(int argc, char *argv[])
                                        last_q_sample=dqsample;
                                        if (j == 62) bp+=8; // skip 8 SYNC/C&C bytes of second block
                                  }
+                                fprintf(stderr,"LEVEL=%f\n", sum*0.007936508);
                                 // wrap-around of ring buffer
                                  if (txptr >= RTXLEN) txptr=0;
                                }
                                break;
 
-                               // respond to an incoming Metis detection request
-                               case 0x0002feef:
+                           // respond to an incoming Metis detection request
+                           case 0x0002feef:
 
                                fprintf(stderr, "Respond to an incoming Metis detection request / code: 0x%08x\n", code);
 
@@ -589,13 +584,13 @@ int main(int argc, char *argv[])
                                }
                                else
                                {
-                                       sendto(sock_ep2, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                       sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
                                }
 
                                break;
 
-                               // stop the Red Pitaya to PC transmission via handler_ep6
-                               case 0x0004feef:
+                           // stop the Red Pitaya to PC transmission via handler_ep6
+                           case 0x0004feef:
 
                                fprintf(stderr, "STOP the transmission via handler_ep6 / code: 0x%08x\n", code);
 
@@ -616,18 +611,16 @@ int main(int argc, char *argv[])
                                }
                                break;
 
-                               // start the Red Pitaya to PC transmission via handler_ep6
-                               case 0x1104feef:
-                               //// This special code 0x11 is no longer needed, is does exactly the same thing
-                               //// as the other start codes 0x01, 0x02, 0x03
+                           // 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:
+                           case 0x0104feef:
+                           case 0x0204feef:
+                           case 0x0304feef:
 
                                fprintf(stderr, "START the handler_ep6 thread / code: 0x%08x\n", code);
 
@@ -640,10 +633,10 @@ int main(int argc, char *argv[])
 
                                enable_thread = 0;
                                while (active_thread) usleep(1000);
-                               memset(&addr_ep6, 0, sizeof(addr_ep6));
-                               addr_ep6.sin_family = AF_INET;
-                               addr_ep6.sin_addr.s_addr = addr_from.sin_addr.s_addr;
-                               addr_ep6.sin_port = addr_from.sin_port;
+                               memset(&addr_old, 0, sizeof(addr_old));
+                               addr_old.sin_family = AF_INET;
+                               addr_old.sin_addr.s_addr = addr_from.sin_addr.s_addr;
+                               addr_old.sin_port = addr_from.sin_port;
 
                                //
                                // The initial value of txptr defines the delay between
@@ -659,27 +652,175 @@ int main(int argc, char *argv[])
                                CommonMercuryFreq = 0;
                                if (pthread_create(&thread, NULL, handler_ep6, NULL) < 0)
                                {
-                                       perror("pthread_create");
+                                       perror("create old protocol thread");
                                        return EXIT_FAILURE;
                                }
                                pthread_detach(thread);
                                break;
 
-                               default:
-                               // Possibly a discovery packet of the New protocol, otherwise a severe error
-                               if (bytes_read == 60 && code == 0 && buffer[4] == 0x02)
-                               {
-                                       fprintf(stderr,"NewProtocol discovery packet received (we won't respond)\n");
+                           default:
+                               /*
+                                * Here we end up with several possible packets from the new protocol
+                                * These are processed here.
+                                */
+                               if (bytes_read == 264 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03 && buffer[3] == 0x01) {
+                                 static long cnt=0;
+                                 unsigned long blks=(buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7];
+                                 fprintf(stderr,"OldProtocol Program blks=%lu count=%ld\r", blks, ++cnt);
+                                 usleep(1000);
+                                 memset(buffer, 0, 60);
+                                 buffer[0]=0xEF;
+                                 buffer[1]=0xFE;
+                                 buffer[2]=0x04;
+                                 buffer[3]=0xAA;
+                                 buffer[4]=0xBB;
+                                 buffer[5]=0xCC;
+                                 buffer[6]=0xDD;
+                                 buffer[7]=0xEE;
+                                 buffer[8]=0xFF;
+                                 sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 if (blks == cnt) fprintf(stderr,"\n\n Programming Done!\n");
+                                 break;
+                               }
+                               if (bytes_read == 64 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03 && buffer[3] == 0x02) {
+                                 fprintf(stderr,"OldProtocol Erase packet received:\n");
+                                 sleep(1);
+                                 cnt=0;
+                                 memset(buffer, 0, 60);
+                                 buffer[0]=0xEF;
+                                 buffer[1]=0xFE;
+                                 buffer[2]=0x03;
+                                 buffer[3]=0xAA;
+                                 buffer[4]=0xBB;
+                                 buffer[5]=0xCC;
+                                 buffer[6]=0xDD;
+                                 buffer[7]=0xEE;
+                                 buffer[8]=0xFF;
+                                 sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 break;
+                               
+                               }
+                               if (bytes_read == 63 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03) {
+                                 fprintf(stderr,"OldProtocol SetIP packet received:\n");
+                                 fprintf(stderr,"MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n", buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8]);
+                                 fprintf(stderr,"IP  address is %03d:%03d:%03d:%03d\n", buffer[9], buffer[10], buffer[11], buffer[12]);
+                                 buffer[2]=0x02;
+                                 memset(buffer+9, 0, 54);
+                                 sendto(sock_udp, buffer, 63, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 break;
+                               }
+                               if (code == 0 && buffer[4] == 0x02) {
+                                 fprintf(stderr,"NewProtocol discovery packet received\n");
+                                 // prepeare response
+                                 memset(buffer, 0, 60);
+                                 buffer [4]=0x02+new_protocol_running();
+                                 buffer [5]=0xAA;
+                                 buffer[ 6]=0xBB;
+                                 buffer[ 7]=0xCC;
+                                 buffer[ 8]=0xDD;
+                                 buffer[ 9]=0xEE;
+                                 buffer[10]=0xFF;
+                                 buffer[11]=DEVICE;
+                                 buffer[12]=38;
+                                 buffer[13]=103;
+                                 buffer[20]=2;
+                                 buffer[21]=1;
+                                 buffer[22]=3;
+                                 sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 break;
+                               }
+                               if (code == 0 && buffer[4] == 0x04) {
+                                 fprintf(stderr,"NewProtocol erase packet received\n");
+                                  memset(buffer, 0, 60);
+                                  buffer [4]=0x02+active_thread;
+                                  buffer [5]=0xAA;
+                                  buffer[ 6]=0xBB;
+                                  buffer[ 7]=0xCC;
+                                  buffer[ 8]=0xDD;
+                                  buffer[ 9]=0xEE;
+                                  buffer[10]=0xFF;
+                                  buffer[11]=DEVICE;
+                                  buffer[12]=38;
+                                  buffer[13]=103;
+                                  buffer[20]=2;
+                                  buffer[21]=1;
+                                  buffer[22]=3;
+                                  sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 sleep(5); // pretend erase takes 5 seconds
+                                  sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 break;
+                               }
+                               if (bytes_read == 265 && buffer[4] == 0x05) {
+                                 unsigned long seq, blk;
+                                 seq=(buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+                                 blk=(buffer[5] << 24) + (buffer[6] << 16) + (buffer[7] << 8) + buffer[8];
+                                 fprintf(stderr,"NewProtocol Program packet received: seq=%lu blk=%lu\r",seq,blk);
+                                 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 [5]=0xAA;
+                                  buffer[ 6]=0xBB;
+                                  buffer[ 7]=0xCC;
+                                  buffer[ 8]=0xDD;
+                                  buffer[ 9]=0xEE;
+                                  buffer[10]=0xFF;
+                                 buffer[11]=103;
+                                 buffer[12]=DEVICE;
+                                 buffer[13]=(checksum >> 8) & 0xFF;
+                                 buffer[14]=(checksum     ) & 0xFF;
+                                 sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 if (seq+1 == blk) fprintf(stderr,"\n\nProgramming Done!\n");
+                                 break;
+                               }
+                               if (bytes_read == 60 && code == 0 && buffer[4] == 0x06) {
+                                 fprintf(stderr,"NewProtocol SetIP packet received for MAC %2x:%2x:%2x:%2x%2x:%2x IP=%d:%d:%d:%d\n",
+                                          buffer[5],buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],
+                                         buffer[11],buffer[12],buffer[13],buffer[14]);
+                                 // only respond if this is for OUR device
+                                 if (buffer[ 5] != 0xAA) break;
+                                 if (buffer[ 6] != 0xBB) break;
+                                 if (buffer[ 7] != 0xCC) break;
+                                 if (buffer[ 8] != 0xDD) break;
+                                 if (buffer[ 9] != 0xEE) break;
+                                 if (buffer[10] != 0xFF) break;
+                                  memset(buffer, 0, 60);
+                                  buffer [4]=0x02+active_thread;
+                                  buffer [5]=0xAA;
+                                  buffer[ 6]=0xBB;
+                                  buffer[ 7]=0xCC;
+                                  buffer[ 8]=0xDD;
+                                  buffer[ 9]=0xEE;
+                                  buffer[10]=0xFF;
+                                  buffer[11]=DEVICE;
+                                  buffer[12]=38;
+                                  buffer[13]=103;
+                                  buffer[20]=2;
+                                  buffer[21]=1;
+                                  buffer[22]=3;
+                                  sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                                 break;
+                               }
+                               if (bytes_read == 60 && buffer[4] == 0x00) {
+                                 // handle general packet
+                                 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;
+                                 addr_new.sin_port = addr_from.sin_port;
+                                 new_protocol_general_packet(buffer);
+                                 break;
                                }
                                else
                                {
-                                       fprintf(stderr,"PackedInvalidCode: 0x%08x (Len=%d)\n", code, bytes_read);
+                                       fprintf(stderr,"Invalid packet (len=%d) detected: ",bytes_read);
+                                       for (i=0; i<16; i++) fprintf(stderr,"%02x ",buffer[i]);
+                                       fprintf(stderr,"\n");
                                }
                                break;
                        }
        }
 
-       close(sock_ep2);
+       close(sock_udp);
 
        if (sock_TCP_Client > -1)
        {
@@ -728,7 +869,7 @@ void process_ep2(uint8_t *frame)
           chk_data(((frame[4] >> 6) & 1), MicTS, "TimeStampMic");
           chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq");
 
-         if (isc25) {
+         if (DEVICE == DEVICE_C25) {
               // Charly25: has two 18-dB preamps that are switched with "preamp" and "dither"
               //           and two attenuators encoded in Alex-ATT
              //           Both only applies to RX1!
@@ -793,7 +934,7 @@ void process_ep2(uint8_t *frame)
           chk_data(frame[3] & 0x80,alexTRdisable,"ALEX T/R disable");
           chk_data(frame[4],alex_lpf,"ALEX LPF");
            // reset TX level. Leve a little head-room for noise
-          txdrv_dbl=(double) txdrive / 256.0;
+          txdrv_dbl=(double) txdrive * 0.00390625;  // div. by. 256
           break;
 
        case 20:
@@ -817,11 +958,7 @@ void process_ep2(uint8_t *frame)
           chk_data((frame[4] & 0x1F) >> 0, rx_att[0], "RX1 ATT");
           chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable");
 
-#ifdef MICSAMPLES
-         if (rx_att[0] > 20 && micsamples_ptr == -1) micsamples_ptr =  0;
-         if (rx_att[0] < 10 && micsamples_ptr == 48000) micsamples_ptr = -1;
-#endif
-          if (!isc25) {
+          if (DEVICE != DEVICE_C25) {
             // Set RX amplification factors. No switchable preamps available normally.
              rxatt_dbl[0]=pow(10.0, -0.05*(10*AlexAtt+rx_att[0]));
              rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]));
@@ -861,7 +998,7 @@ void process_ep2(uint8_t *frame)
             chk_data((frame[2] & 0x30) >> 4, rx_adc[6], "RX7 ADC");
            chk_data((frame[3] & 0x1f), txatt, "TX ATT");
            txatt_dbl=pow(10.0, -0.05*(double) txatt);
-           if (isc25) {
+           if (DEVICE == DEVICE_C25) {
                // RedPitaya: Hard-wired ADC settings.
                rx_adc[0]=0;
                rx_adc[1]=1;
@@ -885,15 +1022,6 @@ void process_ep2(uint8_t *frame)
        }
 }
 
-//
-// The "RX" signal. This is some noise, and some frequencies
-//
-#define LENNOISE 48000
-#define NOISEDIV (RAND_MAX / 24000)
-
-static double noiseItab[LENNOISE];
-static double noiseQtab[LENNOISE];
-
 static double T0800Itab[480];
 static double T0800Qtab[480];
 static double T2000Itab[192];
@@ -904,11 +1032,8 @@ void *handler_ep6(void *arg)
        int i, j, k, n, size;
        int data_offset, header_offset;
        uint32_t counter;
-       uint16_t audio[512];
        uint8_t buffer[1032];
        uint8_t *pointer;
-       struct iovec iovec;
-       struct msghdr msghdr;
        uint8_t id[4] = { 0xef, 0xfe, 1, 6 };
        uint8_t header[40] =
        {
@@ -936,17 +1061,7 @@ void *handler_ep6(void *arg)
         double run,inc;
         double i1,q1,fac;
 
-       memset(audio, 0, sizeof(audio));
-       memset(&iovec, 0, sizeof(iovec));
-       memset(&msghdr, 0, sizeof(msghdr));
-
-       memcpy(buffer + i * 1032, id, 4);
-       iovec.iov_base = buffer;
-       iovec.iov_len = 1032;
-       msghdr.msg_iov = &iovec;
-       msghdr.msg_iovlen = 1;
-       msghdr.msg_name = &addr_ep6;
-       msghdr.msg_namelen = sizeof(addr_ep6);
+       memcpy(buffer, id, 4);
 
        header_offset = 0;
        counter = 0;
@@ -954,13 +1069,6 @@ void *handler_ep6(void *arg)
         //
         // Produce RX data
         //
-        // a) noise from a 16-bit ADC
-        //
-        k=RAND_MAX / 2;
-        for (i=0; i<LENNOISE; i++) {
-         noiseItab[i]= ((double) rand() / k - 1.0) * 0.00003;
-         noiseQtab[i]= ((double) rand() / k - 1.0) * 0.00003;
-        }
        noiseIQpt=0;
        //
        // b) some tones in the upper side band (one wave)
@@ -1021,7 +1129,7 @@ void *handler_ep6(void *arg)
                    memset(pointer, 0, 504);
                    for (j=0; j<n; j++) {
                        // ADC1: noise + weak tone on RX, feedback sig. on TX (except STEMlab)
-                       if (ptt && !isc25) {
+                       if (ptt && (DEVICE != DEVICE_C25)) {
                          i1=isample[rxptr]*txdrv_dbl;
                          q1=qsample[rxptr]*txdrv_dbl;
                          fac=IM3a+IM3b*(i1*i1+q1*q1);
@@ -1034,7 +1142,7 @@ void *handler_ep6(void *arg)
                          adc1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
                        }
                        // ADC2: noise + stronger tone on RX, feedback sig. on TX (only STEMlab)
-                       if (ptt && isc25) {
+                       if (ptt && (DEVICE == DEVICE_C25)) {
                          i1=isample[rxptr]*txdrv_dbl;
                          q1=qsample[rxptr]*txdrv_dbl;
                          fac=IM3a+IM3b*(i1*i1+q1*q1);
@@ -1069,17 +1177,17 @@ void *handler_ep6(void *arg)
                                myqsample=0;
                                break;
                            }
-                           if (ismetis && ptt && (k==1)) {
+                           if (DEVICE == DEVICE_ATLAS && ptt && (k==1)) {
                                // METIS: TX DAC signal goes to RX2 when TXing
                                myisample=dacisample;
                                myqsample=dacqsample;
                            }
-                           if (ishermes && ptt && (k==3)) {
+                           if ((DEVICE==DEVICE_HERMES || DEVICE==DEVICE_HERMES2 || DEVICE==DEVICE_C25) && ptt && (k==3)) {
                                // HERMES: TX DAC signal goes to RX4 when TXing
                                myisample=dacisample;
                                myqsample=dacqsample;
                            }
-                           if (isorion && ptt && (k==4)) {
+                           if ((DEVICE==DEVICE_ANGELIA || DEVICE == DEVICE_ORION || DEVICE== DEVICE_ORION2) && ptt && (k==4)) {
                                // ANGELIA and beyond: TX DAC signal goes to RX5 when TXing
                                myisample=dacisample;
                                myqsample=dacqsample;
@@ -1091,24 +1199,8 @@ void *handler_ep6(void *arg)
                            *pointer++ = (myqsample >>  8) & 0xFF;
                            *pointer++ = (myqsample >>  0) & 0xFF;
                        }
-#ifdef MICSAMPLES
-                       if (micsamples_ptr >= 0 && micsamples_ptr < 48000) {
-                         ssample=micsamples[micsamples_ptr];
-                         *pointer++ = (ssample >> 8) & 0xFF;
-                         *pointer++ = (ssample     ) & 0xFF;
-                         micsamples_rate++;
-                         if (micsamples_rate == 1 << rate) {
-                               micsamples_rate=0;
-                               micsamples_ptr++;
-                         }
-                       } else {
-                         *pointer++ = 0;
-                         *pointer++ = 0;
-                       }
-#else
                        // Microphone samples: silence
                        pointer += 2;
-#endif
                        rxptr++;     if (rxptr >= RTXLEN) rxptr=0;
                        noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV;
                        pt2000++;    if (pt2000 == len2000) pt2000=0;
@@ -1144,14 +1236,14 @@ void *handler_ep6(void *arg)
 
                if (sock_TCP_Client > -1)
                {
-                       if (sendmsg(sock_TCP_Client, &msghdr, 0) < 0)
+                       if (sendto(sock_TCP_Client, buffer, 1032, 0, (struct sockaddr *)&addr_old, sizeof(addr_old)) < 0)
                        {
                                fprintf(stderr, "TCP sendmsg error occurred at sequence number: %u !\n", counter);
                        }
                }
                else
                {
-                       sendmsg(sock_ep2, &msghdr, 0);
+                       sendto(sock_udp, buffer, 1032, 0, (struct sockaddr *)&addr_old, sizeof(addr_old));
                }
 
        }
@@ -1251,8 +1343,8 @@ void audio_write (int16_t l, int16_t r)
     }
   }
 }
-
-#else
+#endif
+#ifdef ALSASOUND
 //
 // Audio functions based on LINUX ALSA
 //
@@ -1460,3 +1552,18 @@ void audio_write(int16_t left_sample,int16_t right_sample) {
   }
 }
 #endif
+
+//
+// Dummy audio functions if this is compiled without audio support
+//
+#ifdef NEED_DUMMY_AUDIO
+void audio_get_cards()
+{
+}
+void audio_open_output()
+{
+}
+void audio_write (int16_t l, int16_t r)
+{
+}
+#endif
diff --git a/newhpsdrsim.c b/newhpsdrsim.c
new file mode 100644 (file)
index 0000000..cf96e73
--- /dev/null
@@ -0,0 +1,1247 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <math.h>
+
+extern struct sockaddr_in addr_new;
+extern void audio_write(int16_t r, int16_t l);
+
+#define NUMRECEIVERS 8
+
+#define LENNOISE 192000
+#define NOISEDIV (RAND_MAX / 96000)
+
+extern double noiseItab[LENNOISE];
+extern double noiseQtab[LENNOISE];
+
+#define IM3a  0.60
+#define IM3b  0.20
+
+#define RTXLEN 64512
+#define NEWRTXLEN 64320
+extern double  isample[RTXLEN];  // shared with newhpsdrsim
+extern double  qsample[RTXLEN];  // shared with newhpsdrsim
+static int txptr = 10000;
+
+/*
+ * These variables represent the state of the machine
+ */
+// data from general packet
+static int ddc_port = 0;
+static int duc_port = 0;
+static int hp_port = 0;
+static int shp_port = 0;
+static int audio_port = 0;
+static int duc0_port = 0;
+static int ddc0_port = 0;
+static int mic_port = 0;
+static int wide_port = 0;
+static int wide_enable = 0;
+static int wide_len = 0;
+static int wide_size = 0;
+static int wide_rate = 0;
+static int wide_ppf = 0;
+static int port_mm = 0;
+static int port_smm = 0;
+static int pwm_min = 0;
+static int pwm_max = 0;
+static int bits = 0;
+static int hwtim = 0;
+static int pa_enable = 0;
+static int alex0_enable = 0;
+static int alex1_enable = 0;
+static int mm_port = 0;
+static int smm_port = 0;
+static int iqform = 0;
+
+// data from rx specific packet
+static int adc=0;
+static int adcdither[8];
+static int adcrandom[8];
+static int ddcenable[NUMRECEIVERS];
+static int adcmap[NUMRECEIVERS];
+static int rxrate[NUMRECEIVERS];
+static int syncddc[NUMRECEIVERS];
+
+//data from tx specific packet
+static int dac=0;
+static int cwmode=0;
+static int sidelevel=0;
+static int sidefreq=0;
+static int speed=0;
+static int weight=0;
+static int hang=0;
+static int delay=0;
+static int txrate=0;
+static int ducbits=0;
+static int orion=0;
+static int gain=0;
+static int txatt=0;
+
+//stat from high-priority packet
+static int run=0;
+static int ptt[4];
+static int cwx=0;
+static int dot=0;
+static int dash=0;
+static unsigned long rxfreq[NUMRECEIVERS];
+static unsigned long txfreq=0;
+static int txdrive=0;
+static int w1400=0;  // Xvtr and Audio enable
+static int ocout=0;
+static int db9=0;
+static int mercury_atts=0;
+static int alex0[32];
+static int alex1[32];
+static int stepatt0=0;
+static int stepatt1=0;
+
+//
+// floating point representation of TX-Drive and ADC0-Attenuator
+//
+static double rxatt0_dbl=1.0;
+static double rxatt1_dbl=1.0;
+static double txatt_dbl=1.0;
+static double txdrv_dbl = 0.0;
+
+// End of state variables
+
+static pthread_t ddc_specific_thread_id;
+static pthread_t duc_specific_thread_id;
+static pthread_t rx_thread_id[NUMRECEIVERS];
+static pthread_t tx_thread_id;
+static pthread_t mic_thread_id;
+static pthread_t audio_thread_id;
+static pthread_t highprio_thread_id = 0;
+static pthread_t send_highprio_thread_id;
+
+void   *ddc_specific_thread(void*);
+void   *duc_specific_thread(void*);
+void   *highprio_thread(void*);
+void   *send_highprio_thread(void*);
+void   *rx_thread(void *);
+void   *tx_thread(void *);
+void   *mic_thread(void *);
+void   *audio_thread(void *);
+
+static double txlevel;
+
+int new_protocol_running() {
+  if (run) return 1; else return 0;
+}
+
+void new_protocol_general_packet(unsigned char *buffer) {
+  static unsigned long seqnum=0;
+  unsigned long seqold;
+  int rc;
+
+  seqold = seqnum;
+  seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+  if (seqnum != 0 && seqnum != seqold+1 ) {
+    fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+  }
+
+  rc=(buffer[5] << 8) + buffer[6];
+  if (rc == 0) rc=1025;
+  if (rc != ddc_port) {
+    ddc_port=rc;
+    fprintf(stderr,"GP: RX specific rcv        port is  %4d\n", rc);
+  }
+  rc=(buffer[7] << 8) + buffer[8];
+  if (rc == 0) rc=1026;
+  if (rc != duc_port) {
+    duc_port=rc;
+    fprintf(stderr,"GP: TX specific rcv        port is  %4d\n", rc);
+  }
+  rc=(buffer[9] << 8) + buffer[10];
+  if (rc == 0) rc=1027;
+  if (rc != hp_port) {
+    hp_port=rc;
+    fprintf(stderr,"GP: HighPrio Port rcv      port is  %4d\n", rc);
+  }
+  rc=(buffer[11] << 8) + buffer[12];
+  if (rc == 0) rc=1025;
+  if (rc != shp_port) {
+    shp_port=rc;
+    fprintf(stderr,"GP: HighPrio Port snd      port is  %4d\n", rc);
+  }
+  rc=(buffer[13] << 8) + buffer[14];
+  if (rc == 0) rc=1028;
+  if (rc != audio_port) {
+    audio_port=rc;
+    fprintf(stderr,"GP: Audio rcv              port is  %4d\n", rc);
+  }
+  rc=(buffer[15] << 8) + buffer[16];
+  if (rc == 0) rc=1029;
+  if (rc != duc0_port) {
+    duc0_port=rc;
+    fprintf(stderr,"GP: TX data rcv base       port is  %4d\n", rc);
+  }
+  rc=(buffer[17] << 8) + buffer[18];
+  if (rc == 0) rc=1035;
+  if (rc != ddc0_port) {
+    ddc0_port=rc;
+    fprintf(stderr,"GP: RX data snd base       port is  %4d\n", rc);
+  }
+  rc=(buffer[19] << 8) + buffer[20];
+  if (rc == 0) rc=1026;
+  if (rc != mic_port) {
+    mic_port=rc;
+    fprintf(stderr,"GP: Microphone data snd    port is  %4d\n", rc);
+  }
+  rc=(buffer[21] << 8) + buffer[22];
+  if (rc == 0) rc=1027;
+  if (rc != wide_port) {
+    wide_port=rc;
+    fprintf(stderr,"GP: Wideband data snd       port is  %4d\n", rc);
+  }
+  rc=buffer[23]; 
+  if (rc != wide_enable) {
+    wide_enable = rc;
+    fprintf(stderr,"GP: Wideband Enable Flag is %d\n", rc);
+  }
+  rc=(buffer[24] << 8) + buffer[25]; if (rc == 0) rc=512;
+  if (rc != wide_len) {
+    wide_len=rc;
+    fprintf(stderr,"GP: WideBand Length is %d\n", rc);
+  }
+  rc=buffer[26]; if (rc == 0) rc=16;
+  if (rc != wide_size) {
+    wide_size=rc;
+    fprintf(stderr,"GP: Wideband sample size is %d\n", rc);
+  }
+  rc=buffer[27];
+  if (rc != wide_rate) {
+    wide_rate=rc;
+    fprintf(stderr,"GP: Wideband sample rate is %d\n", rc);
+  }
+  rc=buffer[28];
+  if (rc != wide_ppf) {
+    wide_ppf = rc;
+    fprintf(stderr,"GP: Wideband PPF is %d\n", rc);
+  }
+  rc = (buffer[29] << 8) + buffer[30];
+  if (rc != port_mm) {
+    port_mm=rc;
+    fprintf(stderr,"MemMapped Registers rcv port is %d\n", rc);
+  }
+  rc = (buffer[31] << 8) + buffer[32];
+  if (rc != port_smm) {
+    port_smm=rc;
+    fprintf(stderr,"MemMapped Registers snd port is %d\n", rc);
+  }
+  rc = (buffer[33] << 8) + buffer[34];
+  if (rc != pwm_min) {
+    pwm_min=rc;
+    fprintf(stderr,"GP: PWM Min value is %d\n", rc);
+  }
+  rc = (buffer[35] << 8) + buffer[36];
+  if (rc != pwm_max) {
+    pwm_max=rc;
+    fprintf(stderr,"GP: PWM Max value is %d\n", rc);
+  }
+  rc=buffer[37];
+  if (rc != bits) {
+    bits=rc;
+    fprintf(stderr,"GP: ModeBits=x%02x\n", rc);
+  }
+  rc=buffer[38];
+  if (rc != hwtim) {
+    hwtim=rc;
+    fprintf(stderr,"GP: Hardware Watchdog enabled=%d\n", rc);
+  }
+
+  iqform = buffer[39];                         if (iqform == 0) iqform=3;
+  if (iqform != 3) fprintf(stderr,"GP: Wrong IQ Format requested: %d\n",iqform);
+
+  rc = (buffer[58] & 0x01);
+  if (rc != pa_enable) {
+    pa_enable=rc;
+    fprintf(stderr,"GP: PA enabled=%d\n", rc);
+  }
+
+  rc=buffer[59] & 0x01;
+  if (rc != alex0_enable) {
+    alex0_enable=rc;
+    fprintf(stderr,"GP: ALEX0 register enable=%d\n", rc);
+  }
+  rc=(buffer[59] & 0x02) >> 1;
+  if (rc != alex1_enable) {
+    alex1_enable=rc;
+    fprintf(stderr,"GP: ALEX1 register enable=%d\n", rc);
+  }
+  //
+  // Start HighPrio thread if we arrive here for the first time
+  // The HighPrio thread keeps running all the time.
+  //
+  if (!highprio_thread_id) {
+       if (pthread_create(&highprio_thread_id, NULL, highprio_thread, NULL) < 0) {
+         perror("***** ERROR: Create HighPrio thread");
+       }
+       pthread_detach(highprio_thread_id);
+
+  //
+  // init state arrays to zero for the first time
+  //
+  memset(adcdither, 0, 8*sizeof(int));
+  memset(adcrandom, 0, 8*sizeof(int));
+  memset(ddcenable, 0, NUMRECEIVERS*sizeof(int));
+  memset(adcmap, 0, NUMRECEIVERS*sizeof(int));
+  memset(syncddc, 0, NUMRECEIVERS*sizeof(int));
+
+  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));
+  }
+}
+
+void *ddc_specific_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t lenaddr = sizeof(addr);
+  unsigned long seqnum,seqold;
+  struct timeval tv;
+  unsigned char buffer[2000];
+  int yes = 1;
+  int rc;
+  int i,j;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: RX specific: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+  tv.tv_sec = 0;
+  tv.tv_usec = 10000;
+  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(ddc_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: RX specific: bind");
+    return NULL;
+  }
+
+  while(run) {
+     rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+     if (rc < 0 && errno != EAGAIN) {
+       perror("***** ERROR: DDC specific thread: recvmsg");
+       break;
+     }
+     if (rc < 0) continue;
+     if (rc != 1444) {
+       fprintf(stderr,"RX: Received DDC specific packet with incorrect length");
+       break;
+     }
+     seqold = seqnum;
+     seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+     if (seqnum != 0 &&seqnum != seqold+1 ) {
+       fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+     }
+     if (adc != buffer[4]) {
+       adc=buffer[4];
+       fprintf(stderr,"RX: Number of ADCs: %d\n",adc);
+     }
+     for (i=0; i<8; 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++) {
+       rc=(buffer[6] >> i) & 0x01;
+       if (rc != adcrandom[i]) {
+         adcrandom[i]=rc;
+        fprintf(stderr,"RX: ADC%d random=%d\n",i,rc);
+       }
+     }
+
+     for (i=0; i<NUMRECEIVERS; i++) {
+       int modified=0;
+
+       rc=(buffer[7 + (i/8)] >> (i % 8)) & 0x01;
+       if (rc != ddcenable[i]) {
+        modified=1;
+        ddcenable[i]=rc;
+       }
+       
+       rc=buffer[17+6*i];
+       if (rc != adcmap[i]) {
+        modified=1;
+        adcmap[i]=rc;
+       }
+
+       rc=(buffer[18+6*i] << 8) + buffer[19+6*i];
+       if (rc != rxrate[i]) {
+        modified=1;
+        rxrate[i]=rc;
+         modified=1;
+       }
+
+       if (syncddc[i] != buffer[1363+i]) {
+        syncddc[i]=buffer[1363+i];
+         modified=1;
+       }
+       rc=(buffer[7 + (i/8)] >> (i % 8)) & 0x01;
+       if (rc != ddcenable[i]) {
+        modified=1;
+        ddcenable[i]=rc;
+       }
+       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]);
+       }
+     }
+  }
+  close(sock);
+  return NULL;
+}
+
+void *duc_specific_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t lenaddr=sizeof(addr);
+  unsigned long seqnum,seqold;
+  struct timeval tv;
+  unsigned char buffer[100];
+  int yes = 1;
+  int rc;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: TX specific: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+  tv.tv_sec = 0;
+  tv.tv_usec = 10000;
+  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(duc_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: TX specific: bind");
+    return NULL;
+  }
+
+  while(run) {
+     rc = recvfrom(sock, buffer, 60, 0,(struct sockaddr *)&addr, &lenaddr);
+     if (rc < 0 && errno != EAGAIN) {
+       perror("***** ERROR: DUC specific thread: recvmsg");
+       break;
+     }
+     if (rc < 0) continue;
+     if (rc != 60) {
+       fprintf(stderr,"TX: DUC Specific: wrong length\n");
+        break;
+     }
+     seqold = seqnum;
+     seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+     if (seqnum != 0 &&seqnum != seqold+1 ) {
+       fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+     }
+     if (dac != buffer[4]) {
+       dac=buffer[4];
+       fprintf(stderr,"TX: Number of DACs: %d\n", dac);
+     }
+     if (cwmode != buffer[5]) {
+       cwmode=buffer[5];
+       fprintf(stderr,"TX: CW mode bits = %x\n",cwmode);
+     }
+     if (sidelevel != buffer[6]) {
+       sidelevel=buffer[6];
+       fprintf(stderr,"TX: CW side tone level: %d\n", sidelevel);
+     }
+     rc=(buffer[7] << 8) + buffer[8];
+     if (rc != sidefreq) {
+       sidefreq = rc;
+       fprintf(stderr,"TX: CW sidetone freq: %d\n", sidefreq);
+     }
+     if (speed != buffer[9]) {
+       speed = buffer[9];
+       fprintf(stderr,"TX: CW keyer speed: %d wpm\n", speed);
+     }
+     if (weight != buffer[10]) {
+       weight=buffer[10];
+       fprintf(stderr,"TX: CW weight: %d\n", weight);
+     }
+     rc=(buffer[11] << 8) + buffer[12];
+     if (hang != rc) {
+       hang = rc;
+       fprintf(stderr,"TX: CW hang time: %d msec\n", hang);
+     }
+     if (delay != buffer[13]) {
+       delay=buffer[13];
+       fprintf(stderr,"TX: RF delay: %d msec\n", delay);
+     }
+     rc=(buffer[14] << 8) + buffer[15];
+     if (txrate != rc) {
+       txrate = rc;
+       fprintf(stderr,"TX: DUC sample rate: %d\n", rc);
+     }
+     if (ducbits != buffer[16]) {
+       ducbits=buffer[16];
+       fprintf(stderr,"TX: DUC sample width: %d bits\n", ducbits);
+     }
+     if (orion != buffer[50]) {
+       orion=buffer[50];
+       fprintf(stderr,"TX: ORION bits (mic etc): %x\n", orion);
+     }
+     if (gain != buffer[51]) {
+       gain= buffer[51];
+       fprintf(stderr,"TX: LineIn Gain (dB): %f\n", 12.0 - 1.5*gain);
+     }
+     if (txatt != buffer[59]) {
+       txatt = buffer[59];
+       txatt_dbl=pow(10.0, -0.05*txatt);
+       fprintf(stderr,"TX: ATT DUC0/ADC0: %d\n", txatt);
+     }
+  }
+  close(sock);
+  return NULL;
+}
+
+void *highprio_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t lenaddr=sizeof(addr);
+  unsigned long seqnum,seqold;
+  unsigned char buffer[2000];
+  struct timeval tv;
+  int yes = 1;
+  int rc;
+  unsigned long freq;
+  int i;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: HP: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+  tv.tv_sec = 0;
+  tv.tv_usec = 10000;
+  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(hp_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: HP: bind");
+    return NULL;
+  }
+
+  while(1) {
+     rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+     if (rc < 0 && errno != EAGAIN) {
+       perror("***** ERROR: HighPrio thread: recvmsg");
+       break;
+     }
+     if (rc < 0) continue;
+     if (rc != 1444) {
+       fprintf(stderr,"Received HighPrio packet with incorrect length");
+       break;
+     }
+     seqold = seqnum;
+     seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+     if (seqnum != 0 &&seqnum != seqold+1 ) {
+       fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+     }
+     rc=(buffer[4] >> 0) & 0x01;
+     if (rc != run) {
+       run=rc;
+       fprintf(stderr,"HP: Run=%d\n", rc);
+        // if run=0, wait for threads to complete, otherwise spawn them off
+        if (run) {
+          if (pthread_create(&ddc_specific_thread_id, NULL, ddc_specific_thread, NULL) < 0) {
+            perror("***** ERROR: Create DDC specific thread");
+          }
+          if (pthread_create(&duc_specific_thread_id, NULL, duc_specific_thread, NULL) < 0) {
+            perror("***** ERROR: Create DUC specific thread");
+          }
+          for (i=0; i< NUMRECEIVERS; i++) {
+            if (pthread_create(&rx_thread_id[i], NULL, rx_thread, (void *) (uintptr_t) i) < 0) {
+              perror("***** ERROR: Create RX thread");
+            }
+         }
+          if (pthread_create(&tx_thread_id, NULL, tx_thread, NULL) < 0) {
+            perror("***** ERROR: Create TX thread");
+          }
+          if (pthread_create(&send_highprio_thread_id, NULL, send_highprio_thread, NULL) < 0) {
+            perror("***** ERROR: Create SendHighPrio thread");
+          }
+          if (pthread_create(&mic_thread_id, NULL, mic_thread, NULL) < 0) {
+            perror("***** ERROR: Create Mic thread");
+          }
+          if (pthread_create(&audio_thread_id, NULL, audio_thread, NULL) < 0) {
+            perror("***** ERROR: Create Audio thread");
+          }
+        } else {
+          pthread_join(ddc_specific_thread_id, NULL);
+          pthread_join(duc_specific_thread_id, NULL);
+          for (i=0; i<NUMRECEIVERS; i++) {
+            pthread_join(rx_thread_id[i], NULL);
+          }
+          pthread_join(send_highprio_thread_id, NULL);
+          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[5] >> 0) & 0x01;
+     if (rc != cwx) {
+       cwx=rc;
+       fprintf(stderr,"HP: CWX=%d\n", rc);
+     }
+     rc=(buffer[5] >> 1) & 0x01;
+     if (rc != dot) {
+       dot=rc;
+       fprintf(stderr,"HP: DOT=%d\n", rc);
+     }
+     rc=(buffer[5] >> 2) & 0x01;
+     if (rc != dash) {
+       dash=rc;
+       fprintf(stderr,"HP: DASH=%d\n", rc);
+     }
+     for (i=0; i<NUMRECEIVERS; i++) {
+       freq=(buffer[ 9+4*i] << 24) + (buffer[10+4*i] << 16) + (buffer[11+4*i] << 8) + buffer[12+4*i];
+        if (bits & 0x08) {
+         freq=round(122880000.0*(double) freq / 4294967296.0);
+       }
+        if (freq != rxfreq[i]) {
+         rxfreq[i]=freq;
+         fprintf(stderr,"HP: DDC%d freq: %lu\n", i, freq);
+       }
+     }
+     freq=(buffer[329] << 24) + (buffer[330] << 16) + (buffer[331] << 8) + buffer[332];
+     if (bits & 0x08) {
+       freq=round(122880000.0*(double) freq / 4294967296.0);
+     }
+     if (freq != txfreq) {
+       txfreq=freq;
+       fprintf(stderr,"HP: DUC freq: %lu\n", freq);
+     }
+     rc=buffer[345];
+     if (rc != txdrive) {
+       txdrive=rc;
+       txdrv_dbl=(double) txdrive * 0.003921568627;
+       fprintf(stderr,"HP: TX drive= %d (%f)\n", txdrive,txdrv_dbl);
+     }
+     rc=buffer[1400];
+     if (rc != w1400) {
+       w1400=rc;
+       fprintf(stderr,"HP: Xvtr/Audio enable=%x\n", rc);
+     }
+     rc=buffer[1401];
+     if (rc != ocout) {
+       ocout=rc;
+       fprintf(stderr,"HP: OC outputs=%x\n", rc);
+     }
+     rc=buffer[1402];
+     if (rc != db9) {
+       db9=rc;
+       fprintf(stderr,"HP: Outputs DB9=%x\n", rc);
+     }
+     rc=buffer[1403];
+     if (rc != mercury_atts) {
+       mercury_atts=rc;
+       fprintf(stderr,"HP: MercuryAtts=%x\n", rc);
+     }
+     // Store Alex0 and Alex1 bits in separate ints
+     freq=(buffer[1428] << 24) + (buffer[1429] << 16) + (buffer[1430] << 8) + buffer[1431];
+     for (i=0; i<32; i++) {
+       rc=(freq >> i) & 0x01;
+       if (rc != alex1[i]) {
+           alex1[i]=rc;
+           fprintf(stderr,"HP: ALEX1 bit%d set to %d\n", i, rc);
+       }
+     }
+     freq=(buffer[1432] << 24) + (buffer[1433] << 16) + (buffer[1434] << 8) + buffer[1435];
+     for (i=0; i<32; i++) {
+       rc=(freq >> i) & 0x01;
+       if (rc != alex0[i]) {
+           alex0[i]=rc;
+           fprintf(stderr,"HP: ALEX0 bit%d set to %d\n", i, rc);
+       }
+     }
+     rc=buffer[1442];
+     if (rc != stepatt1) {
+       stepatt1=rc;
+       rxatt1_dbl=pow(10.0, -0.05*stepatt1);
+       fprintf(stderr,"HP: StepAtt1 = %d\n", rc);
+     }
+     rc=buffer[1443];
+     if (rc != stepatt0) {
+       stepatt0=rc;
+       rxatt0_dbl=pow(10.0, -0.05*stepatt0);
+       fprintf(stderr,"HP: StepAtt0 = %d\n", stepatt0);
+     }
+  }
+  return NULL;
+}
+
+void *rx_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  // One instance of this thread is started for each DDC
+  unsigned long seqnum;
+  unsigned char buffer[1444];
+  int yes = 1;
+  int rc;
+  int ddc;
+  int i;
+  unsigned long time;
+  long wait;
+  double i0sample,q0sample;
+  double i1sample,q1sample;
+  double irsample,qrsample;
+  double fac;
+  int sample;
+  unsigned char *p;
+  int noisept;
+  int myddc;
+  long myrate;
+  int sync,size;
+  int myadc, syncadc;
+  int ps=0;
+  int rxptr;
+  
+  struct timespec delay;
+#ifdef __APPLE__
+  struct timespec now;
+#endif
+
+  myddc=(int) (uintptr_t) data;
+  if (myddc < 0 || myddc >= NUMRECEIVERS) return NULL;
+  seqnum=0;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: RXthread: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(ddc0_port+myddc);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: RXthread: bind");
+    return NULL;
+  }
+
+  noisept=0;
+  clock_gettime(CLOCK_MONOTONIC, &delay);
+  fprintf(stderr,"RX thread %d, enabled=%d\n", myddc, ddcenable[myddc]);
+  rxptr=txptr-5000;  
+  if (rxptr < 0) rxptr += NEWRTXLEN;
+  while (run) {
+       if (!ddcenable[myddc] || rxrate[myddc] == 0 || rxfreq[myddc] == 0) {
+         usleep(5000);
+          clock_gettime(CLOCK_MONOTONIC, &delay);
+         rxptr=txptr-5000;  
+         if (rxptr < 0) rxptr += NEWRTXLEN;
+          continue;
+        }
+       myadc=adcmap[myddc];
+        // for simplicity, we only allow for a single "synchronized" DDC,
+        // this well covers the PURESIGNAL and DIVERSITY cases
+       sync=0;
+       i=syncddc[myddc];
+       while (i) {
+         sync++;
+         i = i >> 1;
+       }
+       // sync == 0 means no synchronizatsion
+        // sync == 1,2,3  means synchronization with DDC0,1,2
+       // Usually we send 238 samples per buffer, but with synchronization
+       // we send 119 sample *pairs*.
+        if (sync) {
+         size=119;
+          wait=119000000L/rxrate[myddc]; // time for these samples in nano-secs
+         syncadc=adcmap[sync-1];
+       } else {
+         size=238;
+          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)
+       //
+         
+        ps=(sync && (rxrate[myadc]==192) && ptt[0] && (syncadc == adc));
+        p=buffer;
+        *p++ =(seqnum >> 24) & 0xFF;
+        *p++ =(seqnum >> 16) & 0xFF;
+        *p++ =(seqnum >>  8) & 0xFF;
+        *p++ =(seqnum >>  0) & 0xFF;
+        seqnum += 1;
+        // do not use time stamps
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       *p++ = 0;
+       // 24 bits per sample *ALWAYS*
+       *p++ = 0;
+       *p++ = 24;
+       *p++ = 0;
+       *p++ = sync ? 2*size : size;  // should be 238 in either case
+       for (i=0; i<size; i++) {
+         //
+         // produce noise depending on the ADC
+         //
+         i1sample=i0sample=noiseItab[noisept];
+         q1sample=q0sample=noiseItab[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) 
+         // b) original TX data (normalized)
+         //
+         if (ps) {
+           irsample = isample[rxptr];
+           qrsample = qsample[rxptr++];
+           if (rxptr >= NEWRTXLEN) rxptr=0;
+           fac=txatt_dbl*txdrv_dbl*(IM3a+IM3b*(irsample*irsample+qrsample*qrsample)*txdrv_dbl*txdrv_dbl);
+           if (myadc == 0) {
+             i0sample += irsample*fac;
+             q0sample += qrsample*fac;
+           }
+         }
+         if (sync) {
+           if (ps) {
+             // synchronized stream: undistorted TX signal with constant max. amplitude
+             i1sample = irsample * 0.329;
+             q1sample = qrsample * 0.329;
+           }
+           sample=i0sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+           sample=q0sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+           sample=i1sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+           sample=q1sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+         } else {
+           sample=i0sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+           sample=q0sample * 8388607.0;
+           *p++=(sample >> 16) & 0xFF;
+           *p++=(sample >>  8) & 0xFF;
+           *p++=(sample >>  0) & 0xFF;
+         }
+        }
+        delay.tv_nsec += wait;
+        while (delay.tv_nsec >= 1000000000) {
+          delay.tv_nsec -= 1000000000;
+          delay.tv_sec++;
+       }
+#ifdef __APPLE__
+        //
+        // The (so-called) operating system for Mac does not have clock_nanosleep(),
+        // but is has clock_gettime as well as nanosleep.
+        // So, to circumvent this problem, we look at the watch and determine
+        // how long we should sleep now.
+        //
+        clock_gettime(CLOCK_MONOTONIC, &now);
+        now.tv_sec =delay.tv_sec  - now.tv_sec;
+        now.tv_nsec=delay.tv_nsec - now.tv_nsec;
+        while (now.tv_nsec < 0) {
+         now.tv_nsec += 1000000000;
+         now.tv_sec--;
+        }
+        nanosleep(&now, NULL);
+#else
+        clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &delay, NULL);
+#endif
+        if (sendto(sock, buffer, 1444, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+          perror("***** ERROR: RX thread sendto");
+          break;
+       }
+  }
+  close(sock);
+  return NULL;
+}
+
+//
+// This thread receives data (TX samples) from the PC
+//
+void *tx_thread(void * data) {
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t lenaddr=sizeof(addr);
+  unsigned long seqnum, seqold;
+  unsigned char buffer[1444];
+  int yes = 1;
+  int rc;
+  int i;
+  unsigned char *p;
+  int noisept;
+  int sample;
+  double di,dq;
+  double sum;
+  struct timeval tv;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: TX: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+  tv.tv_sec = 0;
+  tv.tv_usec = 10000;
+  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(duc0_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: TX: bind");
+    return NULL;
+  }
+
+  seqnum=0; 
+  while(run) {
+     rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+     if (rc < 0 && errno != EAGAIN) {
+       perror("***** ERROR: TX thread: recvmsg");
+       break;
+     }
+     if (rc < 0) continue;
+     if (rc != 1444) {
+       fprintf(stderr,"Received TX packet with incorrect length");
+       break;
+     }
+     seqold = seqnum;
+     seqnum = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+     if (seqnum != 0 &&seqnum != seqold+1 ) {
+       fprintf(stderr,"TXthread: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+     }
+     p=buffer+4;
+     sum=0.0;
+     for (i=0; i<240; i++) {
+       // process 240 TX iq samples
+        sample  = (int)((signed char) (*p++))<<16;
+        sample |= (int)((((unsigned char)(*p++))<<8)&0xFF00);
+        sample |= (int)((unsigned char)(*p++)&0xFF);
+       di = (double) sample / 8388608.0;
+        sample  = (int)((signed char) (*p++))<<16;
+        sample |= (int)((((unsigned char)(*p++))<<8)&0xFF00);
+        sample |= (int)((unsigned char)(*p++)&0xFF);
+       dq = (double) sample / 8388608.0;
+//
+//      put TX samples into ring buffer
+//
+       isample[txptr]=di;
+       qsample[txptr++]=dq;
+       if (txptr >= NEWRTXLEN) txptr=0;
+//
+//      accumulate TX power
+//
+        sum += (di*di+dq*dq);
+     }
+     txlevel=sum * txdrv_dbl * txdrv_dbl * 0.0041667;
+     fprintf(stderr,"LEV1=%f LEV2=%f\n", sum*0.0041667, txlevel);
+  }
+  return NULL;
+}
+
+void *send_highprio_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  unsigned long seqnum;
+  unsigned char buffer[60];
+  int yes = 1;
+  int rc;
+  int i;
+  unsigned char *p;
+
+
+  seqnum=0;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: SendHighPrio thread: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(shp_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: SendHighPrio thread: bind");
+    return NULL;
+  }
+
+  seqnum=0;
+  while (1) {
+    if (!run) {
+       close(sock);
+       break;
+    }
+    // prepare buffer
+    memset(buffer, 0, 60);
+    p=buffer;
+    *p++ = (seqnum >> 24) & 0xFF;
+    *p++ = (seqnum >> 16) & 0xFF;
+    *p++ = (seqnum >>  8) & 0xFF;
+    *p++ = (seqnum >>  0) & 0xFF;
+    *p++ = 0;    // no PTT and CW attached
+    *p++ = 0;    // no ADC overload
+    *p++ = 1;
+    *p++ = 126;    // 1 W exciter power
+    p +=6;
+
+    rc=(int) (800.0*sqrt(10*txlevel));    
+    *p++ = (rc >> 8) & 0xFF;
+    *p++ = (rc     ) & 0xFF;
+
+    buffer[49]=63;   // about 13 volts supply 
+
+    if (sendto(sock, buffer, 60, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+       perror("***** ERROR: HP send thread sendto");
+       break;
+    }
+    seqnum++;
+    usleep(50000); // wait 50 msec
+  }
+  close(sock);
+  return NULL;
+}
+
+//
+// This thread receives the audio samples and plays them
+//
+void *audio_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t lenaddr=sizeof(addr);
+  unsigned long seqnum, seqold;
+  unsigned char buffer[260];
+  int yes = 1;
+  int rc;
+  int i;
+  unsigned char *p;
+  int16_t lsample,rsample;
+  struct timeval tv;
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: Audio: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+  tv.tv_sec = 0;
+  tv.tv_usec = 10000;
+  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(audio_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: Audio: bind");
+    return NULL;
+  }
+
+  seqnum=0;
+  while(run) {
+     rc = recvfrom(sock, buffer, 260, 0,(struct sockaddr *)&addr, &lenaddr);
+     if (rc < 0 && errno != EAGAIN) {
+       perror("***** ERROR: Audio thread: recvmsg");
+       break;
+     }
+     if (rc < 0) continue;
+     if (rc != 260) {
+       fprintf(stderr,"Received Audio packet with incorrect length");
+       break;
+     }
+     seqold = seqnum;
+     seqnum = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+     if (seqnum != 0 &&seqnum != seqold+1 ) {
+       fprintf(stderr,"Audio thread: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+     }
+     p=buffer+4;
+     for (i=0; i<64; i++) {
+       lsample  = ((signed char) *p++) << 8;   
+       lsample |= (*p++ & 0xff); 
+       rsample  = ((signed char) *p++) << 8;   
+       rsample |= (*p++ & 0xff); 
+       audio_write(lsample,rsample);
+    }
+  }
+  close (sock);
+  return NULL;
+}
+
+//
+// The microphone thread generates
+// a two-tone signal
+//
+void *mic_thread(void *data) {
+  int sock;
+  struct sockaddr_in addr;
+  unsigned long seqnum;
+  unsigned char buffer[132];
+  unsigned char *p;
+  int yes = 1;
+  int rc;
+  int i;
+  double arg1,arg2;
+  int sintab[480];  // microphone data
+  int sinptr = 0;
+  struct timespec delay;
+#ifdef __APPLE__
+  struct timespec now;
+#endif
+
+#define FREQ900  0.11780972450961724644234912687298
+#define FREQ1700 0.22252947962927702105777057298230
+
+  seqnum=0;
+  arg1=0.0;
+  arg2=0.0;
+  for (i=0; i<480; i++) {
+    sintab[i]=(int) round((sin(arg1)+sin(arg2)) * 5000.0);
+    arg1 += FREQ900;
+    arg2 += FREQ1700;
+  }
+
+  sock=socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) {
+    perror("***** ERROR: Mic thread: socket");
+    return NULL;
+  }
+
+  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  addr.sin_port = htons(mic_port);
+
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    perror("***** ERROR: Mic thread: bind");
+    return NULL;
+  }
+
+  seqnum=0;
+  memset(buffer, 0, 132);
+  clock_gettime(CLOCK_MONOTONIC, &delay);
+  while (run) {
+    // update seq number
+    p=buffer;
+    *p++ = (seqnum >> 24) & 0xFF;
+    *p++ = (seqnum >> 16) & 0xFF;
+    *p++ = (seqnum >>  8) & 0xFF;
+    *p++ = (seqnum >>  0) & 0xFF;
+    seqnum++;
+    // take periodic data from sintab as "microphone samples"
+    for (i=0; i< 64; i++) {
+       rc=sintab[sinptr++];
+        if (sinptr == 480) sinptr=0;
+        *p++ = (rc >> 8)  & 0xff;
+        *p++ = (rc & 0xff);
+    }
+    // 64 samples with 48000 kHz, makes 1333333 nsec
+    delay.tv_nsec += 1333333;
+    while (delay.tv_nsec >= 1000000000) {
+      delay.tv_nsec -= 1000000000;
+      delay.tv_sec++;
+    }
+#ifdef __APPLE__
+    //
+    // The (so-called) operating system for Mac does not have clock_nanosleep(),
+    // but is has clock_gettime as well as nanosleep.
+    // So, to circumvent this problem, we look at the watch and determine
+    // how long we should sleep now.
+    //
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    now.tv_sec =delay.tv_sec  - now.tv_sec;
+    now.tv_nsec=delay.tv_nsec - now.tv_nsec;
+    while (now.tv_nsec < 0) {
+     now.tv_nsec += 1000000000;
+     now.tv_sec--;
+    }
+    nanosleep(&now, NULL);
+#else
+    clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &delay, NULL);
+#endif
+    if (sendto(sock, buffer, 132, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+      perror("***** ERROR: Mic thread sendto");
+      break;
+    }
+  }
+  close(sock);
+  return NULL;
+}
+  
+