From: c vw <dl1ycf@darc.de>
Date: Wed, 10 Apr 2019 10:12:07 +0000 (+0200)
Subject: Added the new HPSDR simulator program, cool for debugging purposes
X-Git-Url: https://git.rkrishnan.org/pf/content/%22file:/(%5B%5E?a=commitdiff_plain;h=88c18b824f9139998a26f33865f215ccd161969f;p=pihpsdr.git

Added the new HPSDR simulator program, cool for debugging purposes
---

diff --git a/Makefile b/Makefile
index 5449783..2cdbbb4 100644
--- 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
diff --git a/Makefile.mac b/Makefile.mac
index edbb63f..955212a 100644
--- a/Makefile.mac
+++ b/Makefile.mac
@@ -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
index 0000000..55882c7
--- /dev/null
+++ b/hpsdrsim.c
@@ -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;
+}