]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Added the new HPSDR simulator program, cool for debugging purposes
authorc vw <dl1ycf@darc.de>
Wed, 10 Apr 2019 10:12:07 +0000 (12:12 +0200)
committerc vw <dl1ycf@darc.de>
Wed, 10 Apr 2019 10:12:07 +0000 (12:12 +0200)
Makefile
Makefile.mac
hpsdrsim.c [new file with mode: 0755]

index 544978351bfe2fb262bee89ca93ce055541088cb..2cdbbb4ff2de0cef8dd732d3e81933c93f64646e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -445,3 +445,17 @@ release: $(PROGRAM)
        cd release; tar cvf pihpsdr.tar pihpsdr
        cd release; tar cvf pihpsdr-$(GIT_VERSION).tar pihpsdr
 
+#############################################################################
+#
+# hpsdrsim is a cool program that emulates an SDR board with UDP and TCP
+# facilities. It even feeds back the TX signal and distorts it, so that
+# you can test PURESIGNAL.
+# This feature only works if the sample rate is 48000
+#
+#############################################################################
+
+hpsdrsim.o:    hpsdrsim.c
+       $(CC) -c -O hpsdrsim.c
+
+hpsdrsim:      hpsersim.o
+       $(LINK) -o hpsdrsim hpsdrsim.o -lm -lpthread
index edbb63fd1d1dc409d7a2385e4736513c780eb65a..955212abab42fa695f9bc38bfae176894ea76c57 100644 (file)
@@ -434,6 +434,22 @@ release: $(PROGRAM)
        cd release; tar cvf pihpsdr.tar pihpsdr
        cd release; tar cvf pihpsdr-$(GIT_VERSION).tar pihpsdr
 
+
+#############################################################################
+#
+# hpsdrsim is a cool program that emulates an SDR board with UDP and TCP
+# facilities. It even feeds back the TX signal and distorts it, so that
+# you can test PURESIGNAL.
+# This feature only works if the sample rate is 48000
+#
+#############################################################################
+
+hpsdrsim.o:     hpsdrsim.c
+       $(CC) -c -O hpsdrsim.c 
+        
+hpsdrsim:       hpsersim.o
+       $(LINK) -o hpsdrsim hpsdrsim.o -lm -lpthread
+
 #############################################################################
 #
 # This is for MacOS "app" creation ONLY
diff --git a/hpsdrsim.c b/hpsdrsim.c
new file mode 100755 (executable)
index 0000000..55882c7
--- /dev/null
@@ -0,0 +1,1002 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#include <pthread.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#ifndef __APPLE__
+// using clock_nanosleep of librt
+extern int clock_nanosleep(clockid_t __clock_id, int __flags,
+      __const struct timespec *__req,
+      struct timespec *__rem);
+#endif
+
+
+static int sock_TCP_Server = -1;
+static int sock_TCP_Client = -1;
+
+/*
+ * These variables store the state of the SDR.
+ * Whenevery they are changed, this is reported.
+ */
+
+int            AlexTXrel = -1;
+int            alexRXout = -1;
+int            alexRXant = -1;
+int            MicTS = -1;
+int            duplex = -1;
+int            receivers = -1;
+int            rate = -1;
+int             preamp = -1;
+int            LTdither = -1;
+int            LTrandom = -1;
+int            AlexRXant = -1;
+int            AlexRXout = -1;
+int             ref10 = -1;
+int            src122 = -1;
+int            PMconfig = -1;
+int            MicSrc = -1;
+int            txdrive = 0;
+int            txatt = 0;
+int            sidetone_volume = -1;
+int            cw_internal = -1;
+int            rx1_att = -1;
+int            rx1_attE = -1;
+int             rx1_preamp = -1;
+int             rx2_preamp = -1;
+int             rx3_preamp = -1;
+int             rx4_preamp = -1;
+int            MerTxATT0 = -1;
+int            MerTxATT1 = -1;
+int            MetisDB9 = -1;
+int            PeneSel = -1;
+int            PureSignal = -1;
+int            LineGain = -1;
+int            MicPTT = -1;
+int            tip_ring = -1;
+int            MicBias = -1;
+int            ptt=-1;
+int            att=-1;
+int            TX_class_E = -1;
+int            OpenCollectorOutputs=-1;
+long           tx_freq_1=-1;
+long           rx_freq_1=-1;
+long           rx_freq_2=-1;
+long           rx_freq_3=-1;
+long           rx_freq_4=-1;
+long           rx_freq_5=-1;
+long           rx_freq_6=-1;
+long           rx_freq_7=-1;
+int            hermes_config=-1;
+int            alex_lpf=-1;
+int            alex_hpf=-1;
+int            c25_ext_board_i2c_data=-1;
+int            rx1_adc=-1;
+int            rx2_adc=-1;
+int            rx3_adc=-1;
+int            rx4_adc=-1;
+int            rx5_adc=-1;
+int            rx6_adc=-1;
+int            rx7_adc=-1;
+int            cw_hang = -1;
+int            cw_reversed = -1;
+int            cw_speed = -1;
+int            cw_mode = -1;
+int            cw_weight = -1;
+int            cw_spacing = -1;
+int            cw_delay = -1;
+int            CommonMercuryFreq = -1;
+int            rx2_att = -1;
+int             freq=-1;
+
+
+double txdrv_dbl = 1.0;
+double txatt_dbl = 1.0;
+
+int sock_ep2;
+
+struct sockaddr_in addr_ep6;
+
+/*
+ * These two variables monitor whether the TX thread is active
+ */
+int enable_thread = 0;
+int active_thread = 0;
+
+void process_ep2(uint8_t *frame);
+void *handler_ep6(void *arg);
+
+
+/*
+ * The TX data ring buffer
+ */
+
+// 63 * 130,  RTXLEN must be an even multiple of 63!
+#define RTXLEN 8190
+double  isample[RTXLEN];
+double  qsample[RTXLEN];
+int  txptr=0;
+int  rxptr=0;
+
+int main(int argc, char *argv[])
+{
+       int j, size;
+       struct sched_param param;
+       pthread_attr_t attr;
+       pthread_t thread;
+
+       uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
+
+       uint8_t id[4] = { 0xef, 0xfe, 1, 6 };
+       uint32_t code;
+        int16_t  sample;
+
+       struct sockaddr_in addr_ep2, addr_from;
+       uint8_t buffer[1032];
+       struct iovec iovec;
+       struct msghdr msghdr;
+       struct timeval tv;
+       struct timespec ts;
+       int yes = 1;
+        uint8_t *bp;
+
+       uint32_t last_seqnum = 0xffffffff, seqnum;  // sequence number of received packet
+
+       int udp_retries=0;
+       int bytes_read, bytes_left;
+       uint32_t *code0 = (uint32_t *) buffer;  // fast access to code of first buffer
+
+       if ((sock_ep2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+       {
+               perror("socket");
+               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_ep2, 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));
+
+       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);
+
+       if (bind(sock_ep2, (struct sockaddr *)&addr_ep2, sizeof(addr_ep2)) < 0)
+       {
+               perror("bind");
+               return EXIT_FAILURE;
+       }
+
+       if ((sock_TCP_Server = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+       {
+               perror("socket tcp");
+               return EXIT_FAILURE;
+       }
+
+       fprintf(stderr, "DEBUG_TCP: RP <--> PC: sock_TCP_Server: %d\n", sock_TCP_Server);
+
+       setsockopt(sock_TCP_Server, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+
+       int tcpmaxseg = 1032;
+       setsockopt(sock_TCP_Server, IPPROTO_TCP, TCP_MAXSEG, (const char *)&tcpmaxseg, sizeof(int));
+
+       int sndbufsize = 65535;
+       int rcvbufsize = 65535;
+       setsockopt(sock_TCP_Server, SOL_SOCKET, SO_SNDBUF, (const char *)&sndbufsize, sizeof(int));
+       setsockopt(sock_TCP_Server, SOL_SOCKET, SO_RCVBUF, (const char *)&rcvbufsize, sizeof(int));
+       tv.tv_sec = 0;
+       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)
+       {
+               perror("bind tcp");
+               return EXIT_FAILURE;
+       }
+
+       listen(sock_TCP_Server, 1024);
+       fprintf(stderr, "DEBUG_TCP: RP <--> PC: Listening for TCP client connection request\n");
+
+        int flags = fcntl(sock_TCP_Server, F_GETFL, 0);
+        fcntl(sock_TCP_Server, F_SETFL, flags | O_NONBLOCK);
+
+       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;
+
+               if (sock_TCP_Client > -1)
+               {
+                       // Using recvmmsg with a time-out should be used for a byte-stream protocol like TCP
+                       // (Each "packet" in the datagram may be incomplete). This is especially true if the
+                       // socket has a receive time-out, but this problem also occurs if the is no such
+                       // receive time-out.
+                       // Therefore we read a complete packet here (1032 bytes). Our TCP-extension to the
+                       // HPSDR protocol ensures that only 1032-byte packets may arrive here.
+                       bytes_read = 0;
+                       bytes_left = 1032;
+                       while (bytes_left > 0)
+                       {
+                               size = recvfrom(sock_TCP_Client, buffer + bytes_read, (size_t)bytes_left, 0, NULL, 0);
+                               if (size < 0 && errno == EAGAIN) continue;
+                               if (size < 0) break;
+                               bytes_read += size;
+                               bytes_left -= size;
+
+                       }
+
+                       bytes_read=size;
+                       if (size >= 0)
+                       {
+                               // 1032 bytes have successfully been read by TCP.
+                               // Let the downstream code know that there is a single packet, and its size
+                               bytes_read=1032;
+
+                               // In the case of a METIS-discovery packet, change the size to 63
+                               if (*code0 == 0x0002feef)
+                               {
+                                       bytes_read = 63;
+                               }
+
+                               // In principle, we should check on (*code0 & 0x00ffffff) == 0x0004feef,
+                               // then we cover all kinds of start and stop packets.
+
+                               // In the case of a METIS-stop packet, change the size to 64
+                               if (*code0 == 0x0004feef)
+                               {
+                                       bytes_read = 64;
+                               }
+
+                               // In the case of a METIS-start TCP packet, change the size to 64
+                               // The special start code 0x11 has no function any longer, but we shall still support it.
+                               if (*code0 == 0x1104feef || *code0 == 0x0104feef)
+                               {
+                                       bytes_read = 64;
+                               }
+                       }
+               }
+               else
+               {
+                       bytes_read = recvmsg(sock_ep2, &msghdr, 0);
+                       if (bytes_read > 0)
+                       {
+                               udp_retries=0;
+                       }
+                       else
+                       {
+                               udp_retries++;
+                       }
+
+               }
+
+               if (bytes_read < 0 && errno != EAGAIN)
+               {
+                       perror("recvfrom");
+                       return EXIT_FAILURE;
+               }
+
+               // If nothing has arrived via UDP for some time, try to open TCP connection.
+               // "for some time" means 10 subsequent un-successful UDP rcvmmsg() calls
+                if (sock_TCP_Client < 0 && udp_retries > 10)
+                {
+                        if((sock_TCP_Client = accept(sock_TCP_Server, NULL, NULL)) > -1)
+                        {
+                                fprintf(stderr, "DEBUG_TCP: RP <--> PC: sock_TCP_Client: %d connected to sock_TCP_Server: %d\n", sock_TCP_Client, sock_TCP_Server);
+                        }
+                       // This avoids firing accept() too often if it constantly fails
+                       udp_retries=0;
+               }
+               if (bytes_read <= 0) continue;
+                       memcpy(&code, buffer, 4);
+
+                       switch (code)
+                       {
+                               // PC to Red Pitaya transmission via process_ep2
+                               case 0x0201feef:
+
+                               // processing an invalid packet is too dangerous -- skip it!
+                               if (bytes_read != 1032)
+                               {
+                                       fprintf(stderr,"DEBUG_PROT: RvcMsg Code=0x%08x Len=%d\n", code, (int)bytes_read);
+                                       break;
+                               }
+
+                               // sequence number check
+                               seqnum = ((buffer[4]&0xFF)<<24) + ((buffer[5]&0xFF)<<16) + ((buffer[6]&0xFF)<<8) + (buffer[7]&0xFF);
+
+                               if (seqnum != last_seqnum + 1)
+                               {
+                                       fprintf(stderr,"DEBUG_SEQ: SEQ ERROR: last %ld, recvd %ld\n", (long)last_seqnum, (long)seqnum);
+                               }
+
+                               last_seqnum = seqnum;
+
+                                // Put TX IQ samples into the ring buffer
+                               // In the old protocol, samples come in groups of 8 bytes L1 L0 R1 R0 I1 I0 Q1 Q0
+                               // Here, L1/L0 and R1/R0 are audio samples, and I1/I0 and Q1/Q0 are the TX iq samples
+                               // I1 contains bits 8-15 and I0 bits 0-7 of a signed 16-bit integer. We convert this
+                               // here to double
+                                if (ptt) {
+                                   bp=buffer+16;  // skip 8 header and 8 SYNC/C&C bytes
+                                   for (j=0; j<63; j++) {
+                                       bp += 4;  // skip microphone samples
+                                       sample  = (int)((signed char) *bp++)<<8;
+                                       sample |= (int) ((signed char) *bp++ & 0xFF);
+                                        isample[txptr]=(double) sample/ 32768.0;
+                                       sample  = (int)((signed char) *bp++)<<8;
+                                       sample |= (int) ((signed char) *bp++ & 0xFF);
+                                       qsample[txptr]=(double) sample/32768.0;
+                                       txptr++;
+                                   }
+                                   bp+=8; // skip 8  SYNC/C&C bytes of second 512-byte-block
+                                   for (j=0; j<63; j++) {
+                                       bp += 4;  // skip microphone samples
+                                       sample  = (int)((signed char) *bp++)<<8;
+                                       sample |= (int)((signed char) *bp++ & 0xFF);
+                                        isample[txptr]=(double) sample/32768.0;
+                                       sample  = (int)((signed char) *bp++)<<8;
+                                       sample |= (int) ((signed char) *bp++ & 0xFF);
+                                       qsample[txptr]=(double) sample/32768.0;
+                                       txptr++;
+                                   }
+                                } else {
+                                  // put silence into TX buffer
+                                  for (j=0; j<126; j++) {
+                                       isample[txptr]=0.0;
+                                       qsample[txptr++]=0.0;
+                                  }    
+                               }
+                               // wrap-around of ring buffer
+                                if (txptr >= RTXLEN) txptr=0;
+
+                               process_ep2(buffer + 11);
+                               process_ep2(buffer + 523);
+                               break;
+
+                               // respond to an incoming Metis detection request
+                               case 0x0002feef:
+
+                               fprintf(stderr, "DEBUG_PROT: RP -> PC: respond to an incoming Metis detection request / code: 0x%08x\n", code);
+
+                               // processing an invalid packet is too dangerous -- skip it!
+                               if (bytes_read != 63)
+                               {
+                                       fprintf(stderr,"DEBUG_PROT: RvcMsg Code=0x%08x Len=%d\n", code, (int)bytes_read);
+                                       break;
+                               }
+                               reply[2] = 2 + active_thread;
+                               memset(buffer, 0, 60);
+                               memcpy(buffer, reply, 11);
+
+                               // ab-use some of the "unused" bytes in the reply block to indicate we can do TCP
+                               // This information can be used by SDR programs
+                               buffer[11]='T';
+                               buffer[12]='C';
+                               buffer[13]='P';
+                               if (sock_TCP_Client > -1)
+                               {
+                                       // We will get into trouble if we respond via TCP while the radio is
+                                       // running with TCP.
+                                       // We simply suppress the response in this (very unlikely) case.
+                                       if (!active_thread)
+                                       {
+                                           if (send(sock_TCP_Client, buffer, 60, 0) < 0)
+                                           {
+                                               fprintf(stderr, "DEBUG_TCP: RP -> PC: TCP send error occurred when responding to an incoming Metis detection request!\n");
+                                           }
+                                           // close the TCP socket which was only used for the detection
+                                           close(sock_TCP_Client);
+                                           sock_TCP_Client = -1;
+                                       }
+                               }
+                               else
+                               {
+                                       sendto(sock_ep2, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+                               }
+
+                               break;
+
+                               // stop the Red Pitaya to PC transmission via handler_ep6
+                               case 0x0004feef:
+
+                               fprintf(stderr, "DEBUG_PROT: RP -> PC: stop the transmission via handler_ep6 / code: 0x%08x\n", code);
+
+                               // processing an invalid packet is too dangerous -- skip it!
+                               if (bytes_read != 64)
+                               {
+                                       fprintf(stderr,"DEBUG_PROT: RvcMsg Code=0x%08x Len=%d\n", code, bytes_read);
+                                       break;
+                               }
+
+                               enable_thread = 0;
+                               while (active_thread) usleep(1000);
+
+                               if (sock_TCP_Client > -1)
+                               {
+                                       close(sock_TCP_Client);
+                                       sock_TCP_Client = -1;
+                               }
+                               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
+
+                               fprintf(stderr, "DEBUG_TCP: PC -> RP: TCP METIS-start message received / code: 0x%08x\n", code);
+
+                               /* FALLTHROUGH */
+
+                               case 0x0104feef:
+                               case 0x0204feef:
+                               case 0x0304feef:
+
+                               fprintf(stderr, "DEBUG_PROT: RP <--> PC: start the handler_ep6 thread / code: 0x%08x\n", code);
+
+                               // processing an invalid packet is too dangerous -- skip it!
+                               if (bytes_read != 64)
+                               {
+                                       fprintf(stderr,"DEBUG_PROT: RvcMsg Code=0x%08x Len=%d\n", code, bytes_read);
+                                       break;
+                               }
+
+                               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;
+
+                                txptr=3150;  // must be even multiple of 63
+                                rxptr=0;
+                               memset(isample, 0, RTXLEN*sizeof(double));
+                               memset(qsample, 0, RTXLEN*sizeof(double));
+                               enable_thread = 1;
+                               active_thread = 1;
+                               CommonMercuryFreq = 0;
+                               if (pthread_create(&thread, NULL, handler_ep6, NULL) < 0)
+                               {
+                                       perror("pthread_create");
+                                       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,"DEBUG_PROT: PC -> RP: NewProtocol discovery packet received (no response)\n");
+                               }
+                               else
+                               {
+                                       fprintf(stderr,"DEBUG_PROT: PC -> RP: invalid code: 0x%08x (Len=%d)\n", code, bytes_read);
+                               }
+                               break;
+                       }
+       }
+
+       close(sock_ep2);
+
+       if (sock_TCP_Client > -1)
+       {
+               close(sock_TCP_Client);
+       }
+
+       if (sock_TCP_Server > -1)
+       {
+               close(sock_TCP_Server);
+       }
+
+       return EXIT_SUCCESS;
+}
+
+#define chk_data(a,b,c) if ((a) != b) { b = (a); printf("%20s= %08lx (%10ld)\n", c, (long) b, (long) b ); }
+
+void process_ep2(uint8_t *frame)
+{
+
+       uint16_t data;
+
+        chk_data(frame[0] & 1, ptt, "PTT");
+       switch (frame[0])
+       {
+       case 0:
+       case 1:
+         chk_data((frame[1] & 0x03) >> 0, rate, "SampleRate");
+         chk_data((frame[1] & 0x0C) >> 3, ref10, "Ref10MHz");
+         chk_data((frame[1] & 0x10) >> 4, src122, "Source122MHz");
+         chk_data((frame[1] & 0x60) >> 5, PMconfig, "Pen/Mer config");
+         chk_data((frame[1] & 0x80) >> 7, MicSrc, "MicSource");
+
+          chk_data(frame[2] & 1, TX_class_E, "TX CLASS-E");
+          chk_data((frame[2] & 0xfe) >> 1, OpenCollectorOutputs,"OpenCollector");
+
+          chk_data((frame[3] & 0x03) >> 0, att, "ALEX Attenuator");
+          chk_data((frame[3] & 0x04) >> 2, preamp, "ALEX preamp");
+          chk_data((frame[3] & 0x08) >> 2, LTdither, "LT2208 Dither");
+          chk_data((frame[3] & 0x10) >> 2, LTrandom, "LT2208 Random");
+          chk_data((frame[3] & 0x60) >> 5, alexRXant, "ALEX RX ant");
+          chk_data((frame[3] & 0x80) >> 7, alexRXout, "ALEX RX out");
+
+          chk_data(((frame[4] >> 0) & 3), AlexTXrel, "ALEX TX relay");
+          chk_data(((frame[4] >> 2) & 1), duplex,    "DUPLEX");
+          chk_data(((frame[4] >> 3) & 7) + 1, receivers, "RECEIVERS");
+          chk_data(((frame[4] >> 6) & 1), MicTS, "TimeStampMic");
+          chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq");
+         break;
+
+        case 2:
+        case 3:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), tx_freq_1,"TX FREQ");
+          break;
+
+        case 4:
+        case 5:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_1,"RX FREQ1");
+          break;
+
+        case 6:
+        case 7:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_2,"RX FREQ2");
+          break;
+
+        case 8:
+        case 9:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_3,"RX FREQ3");
+          break;
+
+        case 10:
+        case 11:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_4,"RX FREQ4");
+          break;
+
+        case 12:
+        case 13:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_5,"RX FREQ5");
+          break;
+
+        case 14:
+        case 15:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_6,"RX FREQ6");
+          break;
+
+        case 16:
+        case 17:
+          chk_data(frame[4] | (frame[3] << 8) | (frame[2] << 16) | (frame[1] << 24), rx_freq_7,"RX FREQ7");
+          break;
+
+       case 18:
+       case 19:
+          chk_data(frame[1],txdrive,"TX DRIVE");
+          chk_data(frame[2],hermes_config,"HERMES CONFIG");
+          chk_data(frame[3],alex_hpf,"ALEX HPF");
+          chk_data(frame[4],alex_lpf,"ALEX LPF");
+           // reset TX level
+          txdrv_dbl=(double) txdrive / 255.0;
+          break;
+
+       case 20:
+       case 21:
+          chk_data((frame[1] & 0x01) >> 0, rx1_preamp, "RX1 preamp");
+          chk_data((frame[1] & 0x02) >> 1, rx2_preamp, "RX2 preamp");
+          chk_data((frame[1] & 0x04) >> 2, rx3_preamp, "RX3 preamp");
+          chk_data((frame[1] & 0x08) >> 3, rx4_preamp, "RX4 preamp");
+          chk_data((frame[1] & 0x10) >> 4, tip_ring  , "TIP/Ring");
+          chk_data((frame[1] & 0x20) >> 5, MicBias   , "MicBias");
+          chk_data((frame[1] & 0x40) >> 6, MicPTT    , "MicPTT");
+
+          chk_data((frame[2] & 0x1F) >> 0, LineGain  , "LineGain");
+          chk_data((frame[2] & 0x20) >> 5, MerTxATT0 , "Mercury Att on TX/0");
+          chk_data((frame[2] & 0x40) >> 6, PureSignal, "PureSignal");
+          chk_data((frame[2] & 0x80) >> 7, PeneSel   , "PenelopeSelect");
+
+          chk_data((frame[3] & 0x0F) >> 0, MetisDB9  , "MetisDB9");
+          chk_data((frame[3] & 0x10) >> 4, MerTxATT1 , "Mercury Att on TX/1");
+
+          chk_data((frame[4] & 0x1F) >> 0, rx1_att, "RX1 ATT");
+          chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable");
+
+          break;
+
+       case 22:
+       case 23:
+           chk_data(frame[1] & 0x1f, rx2_att,"RX2 ATT");
+           chk_data((frame[2] >> 6) & 1, cw_reversed, "CW REV");
+           chk_data(frame[3] & 63, cw_speed, "CW SPEED");
+           chk_data((frame[3] >> 6) & 3, cw_mode, "CW MODE");
+           chk_data(frame[4] & 127, cw_weight,"CW WEIGHT");
+           chk_data((frame[4] >> 7) & 1, cw_spacing, "CW SPACING");
+          break;
+
+       case 24:
+       case 25:
+           data = frame[1];
+           data |= frame[2] << 8;
+           chk_data((frame[2] << 8) | frame[1], c25_ext_board_i2c_data, "C25 EXT BOARD DATA");
+           break;
+
+       case 28:
+       case 29:
+            chk_data((frame[1] & 0x03) >> 0, rx1_adc, "RX1 ADC");
+            chk_data((frame[1] & 0x0C) >> 2, rx2_adc, "RX2 ADC");
+            chk_data((frame[1] & 0x30) >> 4, rx3_adc, "RX3 ADC");
+            chk_data((frame[1] & 0xC0) >> 6, rx4_adc, "RX4 ADC");
+            chk_data((frame[2] & 0x03) >> 0, rx5_adc, "RX5 ADC");
+            chk_data((frame[2] & 0x0C) >> 2, rx6_adc, "RX6 ADC");
+            chk_data((frame[2] & 0x30) >> 4, rx7_adc, "RX7 ADC");
+           chk_data((frame[3] & 0x1f), txatt, "TX ATT");
+           txatt_dbl=pow(10.0, -0.05*(double) txatt);
+           break;
+
+       case 30:
+       case 31:
+            chk_data(frame[1] & 1,cw_internal,"CW INT");
+            chk_data(frame[2], sidetone_volume,"SIDE TONE VOLUME");
+            chk_data(frame[3], cw_delay,"CW DELAY");
+           cw_delay = frame[3];
+           break;
+
+       case 32:
+       case 33:
+            chk_data((frame[1] << 2) | (frame[2] & 3), cw_hang, "CW HANG");
+            chk_data((frame[3] << 4) | (frame[4] & 255), freq, "SIDE TONE FREQ");
+           break;
+       }
+
+#ifdef DEBUG_EP2
+       fprintf(stderr, "DEBUG_EP2: Frames after switch case statement:\n");
+       fprintf(stderr, "DEBUG_EP2: frame[0]: %d\n", frame[0]);
+       fprintf(stderr, "DEBUG_EP2: frame[1]: %d\n", frame[1]);
+       fprintf(stderr, "DEBUG_EP2: frame[2]: %d\n", frame[2]);
+       fprintf(stderr, "DEBUG_EP2: frame[3]: %d\n", frame[3]);
+       fprintf(stderr, "DEBUG_EP2: frame[4]: %d\n", frame[4]);
+#endif
+}
+
+//
+// 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 T1000Itab[384];
+static double T1000Qtab[384];
+static double T2000Itab[192];
+static double T2000Qtab[192];
+static double T4000Itab[96];
+static double T4000Qtab[96];
+
+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] =
+       {
+               127, 127, 127, 0, 0, 33, 17, 21,
+               127, 127, 127, 8, 0, 0, 0, 0,
+               127, 127, 127, 16, 0, 0, 0, 0,
+               127, 127, 127, 24, 0, 0, 0, 0,
+               127, 127, 127, 32, 66, 66, 66, 66
+       };
+        int32_t sample;
+        struct timespec delay;
+#ifdef __APPLE__
+       struct timespec now;
+#endif
+        int wait;
+        int noiseIQpt;
+       int len4000,pt4000;
+       int len2000,pt2000;
+       int len1000,pt1000;
+        double run,inc;
+        double i1,i2,q1,q2;
+
+       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);
+
+       header_offset = 0;
+       counter = 0;
+
+        //
+        // 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) a 4000, 2000, and 1000  Hz tone in upper side band
+       //
+        len4000=12 << rate; // we must make one sine wave
+        len2000=24 << rate; // we must make one sine wave
+        len1000=48 << rate; // we must make one sine wave
+
+       inc = 6.283185307179586476925287 / (double) len4000;
+       run = 0.0;
+        for (i=0; i<len4000; i++) {
+         T4000Qtab[i]=0.1*cos(run);
+         T4000Itab[i]=0.1*sin(run);
+         run += inc;
+        }
+       inc = 6.283185307179586476925287 / (double) len2000;
+       run = 0.0;
+        for (i=0; i<len2000; i++) {
+         T2000Qtab[i]=0.1*cos(run);
+         T2000Itab[i]=0.1*sin(run);
+         run += inc;
+        }
+       inc = 6.283185307179586476925287 / (double) len1000;
+       run = 0.0;
+        for (i=0; i<len1000; i++) {
+         T1000Qtab[i]=0.1*cos(run);
+         T1000Itab[i]=0.1*sin(run);
+         run += inc;
+        }
+
+        pt4000=0;
+        pt2000=0;
+        pt1000=0;
+         
+        
+        clock_gettime(CLOCK_MONOTONIC, &delay);
+       while (1)
+       {
+               if (!enable_thread) break;
+
+               size = receivers * 6 + 2;
+               n = 504 / size;  // number of samples per 512-byte-block
+                // Time (in nanosecs) to "collect" the samples sent in one sendmsg
+                wait = (2*n*1000000L) / (48 << rate);
+
+                // plug in sequence numbers
+               data_offset = 0;
+               *(uint32_t *)(buffer + 4) = htonl(counter);
+               ++counter;
+
+//
+//             This defines the distortion as well as the amplification
+//
+#define IM3a  0.70
+#define IM3b  0.15
+
+               for (i = 0; i < 2; ++i)
+               {
+                       pointer = buffer + i * 516 - i % 2 * 4 + 8;
+                       memcpy(pointer, header + header_offset, 8);
+
+                       header_offset = header_offset >= 32 ? 0 : header_offset + 8;
+
+                       //
+                       // TODO: HERMES:     Copy TX samples back to RX4, and distorted TX samples to RX3
+                       //                   This allows for testing PURESIGNAL.
+                       //       METIS:      TX to RX2, distorted TX to RX1
+                       //       ANGELIA:    TX to RX5, distroted TX to RX4  (also for ORION and ORION2)
+                       //
+                       pointer += 8;
+                       memset(pointer, 0, 504);
+                       for (j=0; j<n; j++) {
+                         //
+                         // RX1 samples: noise
+                         //
+                         sample= noiseItab[noiseIQpt] * 8388607.0;
+                         *pointer++ = (sample >> 16) & 0xFF;
+                         *pointer++ = (sample >>  8) & 0xFF;
+                         *pointer++ = (sample >>  0) & 0xFF;
+                         sample=noiseQtab[noiseIQpt] * 8388607.0;
+                         *pointer++ = (sample >> 16) & 0xFF;
+                         *pointer++ = (sample >>  8) & 0xFF;
+                         *pointer++ = (sample >>  0) & 0xFF;
+                         if (receivers > 1) {
+                           //
+                           // RX2 samples:
+                            // If transmitting AND samplerate == 48000: RX feedback; else tone 2000
+                           //
+                            if (rate == 0 && ptt) {
+                              // We add some third-order distortion
+                             i1=isample[rxptr]*txdrv_dbl;
+                             q1=qsample[rxptr]*txdrv_dbl;
+                             i2=i1*i1;
+                             q2=q1*q1;
+                              sample= txatt_dbl*i1*(IM3a+IM3b*i2+IM3b*q2) * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample= txatt_dbl*q1*(IM3a+IM3b*q2+IM3b*i2) * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                            } else {
+                              sample=T4000Itab[pt4000] * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample=T4000Qtab[pt4000] * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                            }
+                         }
+                         if (receivers > 2) {
+                           //
+                           // RX3 samples: same as RX2
+                           //
+                           if (rate == 0 && ptt) {
+                              // We add some third-order distortion
+                              i1=isample[rxptr]*txdrv_dbl;
+                              q1=qsample[rxptr]*txdrv_dbl;
+                              i2=i1*i1;
+                              q2=q1*q1;
+                              sample= txatt_dbl*i1*(IM3a+IM3b*i2+IM3b*q2) * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample= txatt_dbl*q1*(IM3a+IM3b*q2+IM3b*i2) * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                            } else {
+                              sample=T4000Itab[pt4000] * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample=T4000Qtab[pt4000] * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                           }
+                         }
+                         if (receivers > 3) {
+                           //
+                           // RX4 samples:
+                           // If transmitting AND samplerate == 48000: TX feedback; else tone 1000
+                           // TX feedback has level independent of txdrv, the value of 0.4 is
+                           // typically used for GetPk
+                           //
+                            if (rate == 0 && ptt) {
+                             // Original TX signal
+                              sample= isample[rxptr] * 0.400 * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample= qsample[rxptr] * 0.400 * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                            } else {
+                              sample=T1000Itab[pt1000] * 0.400 * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                              sample=T1000Qtab[pt1000] * 0.400 * 8388607.0;
+                              *pointer++ = (sample >> 16) & 0xFF;
+                              *pointer++ = (sample >>  8) & 0xFF;
+                              *pointer++ = (sample >>  0) & 0xFF;
+                            }
+                         }
+                         if (size >26) {
+                           // RX5 samples: silence
+                           pointer += 6;
+                         }
+                         if (size >32) {
+                           // RX6 samples: silence
+                           pointer += 6;
+                         }
+                         if (size >38) {
+                           // RX7 samples: silence
+                           pointer += 6;
+                         }
+                         if (size >44) {
+                           // RX8 samples: silence
+                           pointer += 6;
+                         }
+                         // Microphone samples: silence
+                         pointer += 2;
+                         rxptr++;     if (rxptr >= RTXLEN) rxptr=0;
+                         noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV;
+                         pt4000++;    if (pt4000 == len4000) pt4000=0;
+                         pt2000++;    if (pt2000 == len2000) pt2000=0;
+                         pt1000++;    if (pt1000 == len1000) pt1000=0;
+                       }
+               }
+
+               //
+               // Wait until the time has passed for all these samples
+               //
+                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 (sock_TCP_Client > -1)
+               {
+                       if (sendmsg(sock_TCP_Client, &msghdr, 0) < 0)
+                       {
+                               fprintf(stderr, "DEBUG_TCP: RP -> PC: TCP sendmsg error occurred at sequence number: %u !\n", counter);
+                       }
+               }
+               else
+               {
+                       sendmsg(sock_ep2, &msghdr, 0);
+               }
+
+       }
+       active_thread = 0;
+       return NULL;
+}