From 87303e44ee5c001bcd14a12841c99ef3d9cfc547 Mon Sep 17 00:00:00 2001
From: John Wiseman <john.wiseman@cantab.net>
Date: Tue, 15 Oct 2024 14:50:16 +0100
Subject: [PATCH] 6.0.24.45

---
 6pack.cpp                           |  377 ++
 ALSASound.c                         |   84 +-
 BusyDetect.c                        |  168 +-
 Config.cpp                          |   22 +-
 Config.cpp.bak                      |  962 ++--
 Modulate.c                          |   28 +
 QtSoundModem.cpp                    |  314 +-
 QtSoundModem.cpp.bak                | 6760 +++++++++++++--------------
 QtSoundModem.h                      |   24 +
 QtSoundModem.pro                    |    4 +-
 QtSoundModem.pro.user               |  263 ++
 QtSoundModem.pro~                   |   64 +
 QtSoundModem.vcxproj                |    1 +
 QtSoundModem.vcxproj.filters        |    3 +
 QtSoundModem.vcxproj.user           |    8 +-
 SMMain.c                            |   82 +-
 UZ7HOStuff.h                        |   32 +-
 Waveout.c                           |   56 +-
 audio.c                             |    1 +
 ax25.c                              |   15 +-
 ax25_agw.c                          |   70 +-
 ax25_demod.c                        |   11 +-
 ax25_l2.c                           |   54 +-
 ax25_mod.c                          |   11 +
 calibrateDialog.ui                  |   15 +-
 debug/moc_predefs-LAPTOP-Q6S4RP5Q.h |   24 +-
 debug/moc_predefs-notpi4-64.h       |   24 +-
 debug/moc_predefs-skigdebian.h      |   24 +-
 debug/moc_predefs.h                 |    2 +-
 devicesDialog.ui                    |  130 +-
 il2p.c                              |    4 +-
 kiss_mode.c                         |    5 +-
 main.cpp                            |    4 +
 sm_main.c.bak                       | 2862 ++++++------
 tcpCode.cpp                         |  299 +-
 tcpCode.h                           |   20 +-
 36 files changed, 7214 insertions(+), 5613 deletions(-)
 create mode 100644 6pack.cpp
 create mode 100644 QtSoundModem.pro.user
 create mode 100644 QtSoundModem.pro~

diff --git a/6pack.cpp b/6pack.cpp
new file mode 100644
index 0000000..d3ccf68
--- /dev/null
+++ b/6pack.cpp
@@ -0,0 +1,377 @@
+/*
+
+Using code from 6pack Linux Kernel driver with the following licence and credits
+
+ *	6pack driver version 0.4.2, 1999/08/22
+ *
+ *	This module:
+ *		This module is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * 		This module implements the AX.25 protocol for kernel-based
+ *		devices like TTYs. It interfaces between a raw TTY, and the
+ *		kernel's AX.25 protocol layers, just like slip.c.
+ *		AX.25 needs to be separated from slip.c while slip.c is no
+ *		longer a static kernel device since it is a module.
+ *
+ *	Author: Andreas K�nsgen <ajk@ccac.rwth-aachen.de>
+ *
+ *	Lots of stuff has been taken from mkiss.c, written by
+ *	Hans Alblas <hans@esrac.ele.tue.nl>
+ *
+ *	with the fixes from
+ *
+ *	Jonathan (G4KLX)	Fixed to match Linux networking changes - 2.1.15.
+ *	Matthias (DG2FEF)       Fixed bug in ax25_close(): dev_lock_wait() was
+ *                              called twice, causing a deadlock.
+ */
+
+
+ //	6pack needs fast response to received characters, and I want to be able to operate over TCP links as well as serial.
+ //	So I think the character level stuff may need to run in a separate thread, probably using select.
+ //
+ //	I also need to support multiple 6pack ports.
+
+ // ?? Do we add this as a backend to KISS driver or a separate Driver. KISS Driver is already quite messy. Not decided yet.
+
+ // ?? If using serial/real TNC we need to be able to interleave control and data bytes, but I think with TCP/QtSM it won't be necessary
+ // ?? Also a don't see any point in running multiple copies of QtSM on one port, but maybe should treat the QtSM channels as
+ //	multidropped ports for scheduling (?? only if on same radio ??)
+
+ //	?? I think it needs to look like a KISS (L2) driver but will need a transmit scheduler level to do DCD/CSMA/PTT processing,
+ //	ideally with an interlock to other drivers on same port. This needs some thought with QtSM KISS with multiple modems on one channel
+
+
+
+
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+/****************************************************************************
+ *	Defines for the 6pack driver.
+ ****************************************************************************/
+
+#define TRUE	1
+#define FALSE	0
+
+#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
+					   This can be overridden with
+					   insmod -oax25_maxdev=nnn	*/
+#define AX_MTU		236	
+
+					   /* 6pack protocol bytes/masks. */
+#define SIXP_INIT_CMD		0xE8
+#define SIXP_TNC_FOUND		0xE9
+#define SIXP_CMD_MASK		0xC0
+#define SIXP_PRIO_CMD_MASK	0x80
+#define SIXP_PRIO_DATA_MASK	0x38
+#define SIXP_STD_CMD_MASK	0x40
+#define SIXP_DCD_MASK		0x08
+#define SIXP_RX_DCD_MASK	0x18
+#define SIXP_CHN_MASK		0x07
+#define SIXP_TX_MASK		0x20
+#define SIXP_CON_LED_ON		0x68
+#define SIXP_STA_LED_ON		0x70
+#define SIXP_LED_OFF		0x60
+
+/* checksum for a valid 6pack encapsulated packet */
+#define SIXP_CHKSUM		0xFF
+
+/* priority commands */
+#define SIXP_SEOF		0x40	/* TX underrun */
+#define SIXP_TX_URUN		0x48	/* TX underrun */
+#define SIXP_RX_ORUN		0x50	/* RX overrun */
+#define SIXP_RX_BUF_OVL		0x58	/* RX overrun */
+
+struct ax_disp {
+	int                magic;
+
+	char * name;
+	/* Various fields. */
+//	struct tty_struct  *tty;		/* ptr to TTY structure		*/
+//	struct device      *dev;		/* easy for intr handling	*/
+	struct ax_disp     *sixpack;		/* mkiss txport if mkiss channel*/
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char      *rbuff;		/* receiver buffer		*/
+	int                rcount;		/* received chars counter       */
+	unsigned char      *xbuff;		/* transmitter buffer		*/
+	unsigned char      *xhead;		/* pointer to next byte to XMIT */
+	int                xleft;		/* bytes left in XMIT queue     */
+
+	/* SLIP interface statistics. */
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter      */
+	unsigned long      rx_errors;		/* Parity, etc. errors          */
+	unsigned long      tx_errors;		/* Planned stuff                */
+	unsigned long      rx_dropped;		/* No memory for skb            */
+	unsigned long      tx_dropped;		/* When MTU change              */
+	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
+
+	/* Detailed SLIP statistics. */
+	int                 mtu;		/* Our mtu (to spot changes!)   */
+	int                 buffsize;		/* Max buffers sizes            */
+
+
+	unsigned char       flags;		/* Flag values/ mode etc	*/
+#define AXF_INUSE	0		/* Channel in use               */
+#define AXF_ESCAPE	1               /* ESC received                 */
+#define AXF_ERROR	2               /* Parity, etc. error           */
+#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
+#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
+
+	int                 mode;
+
+
+	/* variables for the state machine */
+	unsigned char	tnc_ok;
+	unsigned char	status;
+	unsigned char	status1;
+	unsigned char	status2;
+
+	unsigned char	duplex;
+	unsigned char	led_state;
+	unsigned char	tx_enable;
+
+	unsigned char	raw_buf[4];		/* receive buffer */
+	unsigned char	cooked_buf[400];	/* receive buffer after 6pack decoding */
+
+	unsigned int	rx_count;		/* counter for receive buffer */
+	unsigned int	rx_count_cooked;	/* counter for receive buffer after 6pack decoding */
+
+	unsigned char	tx_delay;
+	unsigned char	persistance;
+	unsigned char	slottime;
+
+};
+
+struct sixpack_channel {
+	int magic;		/* magic word */
+	int init;		/* channel exists? */
+	struct tty_struct *tty; /* link to tty control structure */
+};
+
+#define AX25_MAGIC		0x5316
+#define SIXP_DRIVER_MAGIC	0x5304
+
+#define SIXP_INIT_RESYNC_TIMEOUT	150	/* in 10 ms */
+#define SIXP_RESYNC_TIMEOUT		500	/* in 10 ms */
+
+/* default radio channel access parameters */
+#define SIXP_TXDELAY			25	/* in 10 ms */
+#define SIXP_PERSIST			50
+#define SIXP_SLOTTIME			10	/* in 10 ms */
+
+static int sixpack_encaps(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay);
+static void sixpack_decaps(struct ax_disp *, unsigned char);
+
+static void decode_prio_command(unsigned char, struct ax_disp *);
+static void decode_std_command(unsigned char, struct ax_disp *);
+static void decode_data(unsigned char, struct ax_disp *);
+static void resync_tnc(unsigned long);
+static void xmit_on_air(struct ax_disp *ax);
+static void start_tx_timer(struct ax_disp *ax);
+
+extern "C" void Debugprintf(const char * format, ...);
+
+void Process6PackByte(unsigned char inbyte);
+
+struct ax_disp axdisp;
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+}
+
+
+void Process6PackData(unsigned char * Bytes, int Len)
+{
+	while(Len--)
+		Process6PackByte(Bytes++[0]);
+
+}
+
+void Process6PackByte(unsigned char inbyte)
+{
+	struct ax_disp *ax = &axdisp;
+
+	if (inbyte == SIXP_INIT_CMD)
+	{
+		Debugprintf("6pack: SIXP_INIT_CMD received.\n");
+		{
+			// Reset state machine and allocate a 6pack struct for each modem.
+
+			// reply with INIT_CMD with the channel no of last modem
+		}
+		return;
+	}
+
+	if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
+		decode_prio_command(inbyte, ax);
+	else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
+		decode_std_command(inbyte, ax);
+	else {
+		if ((ax->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
+			decode_data(inbyte, ax);
+	} /* else */
+}
+
+/* identify and execute a 6pack priority command byte */
+
+void decode_prio_command(unsigned char cmd, struct ax_disp *ax)
+{
+	unsigned char channel;
+
+	channel = cmd & SIXP_CHN_MASK;
+	if ((cmd & SIXP_PRIO_DATA_MASK) != 0) {     /* idle ? */
+
+	/* RX and DCD flags can only be set in the same prio command,
+	   if the DCD flag has been set without the RX flag in the previous
+	   prio command. If DCD has not been set before, something in the
+	   transmission has gone wrong. In this case, RX and DCD are
+	   cleared in order to prevent the decode_data routine from
+	   reading further data that might be corrupt. */
+
+		if (((ax->status & SIXP_DCD_MASK) == 0) &&
+			((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
+			if (ax->status != 1)
+				Debugprintf("6pack: protocol violation\n");
+			else
+				ax->status = 0;
+			cmd &= !SIXP_RX_DCD_MASK;
+		}
+		ax->status = cmd & SIXP_PRIO_DATA_MASK;
+	} /* if */
+
+
+		/* if the state byte has been received, the TNC is present,
+		   so the resync timer can be reset. */
+
+	if (ax->tnc_ok == 1) {
+		//		del_timer(&(ax->resync_t));
+		//		ax->resync_t.data = (unsigned long) ax;
+		//		ax->resync_t.function = resync_tnc;
+		//		ax->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT;
+		//		add_timer(&(ax->resync_t));
+	}
+
+	ax->status1 = cmd & SIXP_PRIO_DATA_MASK;
+}
+
+/* try to resync the TNC. Called by the resync timer defined in
+  decode_prio_command */
+
+static void
+resync_tnc(unsigned long channel)
+{
+	static char resync_cmd = SIXP_INIT_CMD;
+	struct ax_disp *ax = (struct ax_disp *) channel;
+
+	Debugprintf("6pack: resyncing TNC\n");
+
+	/* clear any data that might have been received */
+
+	ax->rx_count = 0;
+	ax->rx_count_cooked = 0;
+
+	/* reset state machine */
+
+	ax->status = 1;
+	ax->status1 = 1;
+	ax->status2 = 0;
+	ax->tnc_ok = 0;
+
+	/* resync the TNC */
+
+	ax->led_state = SIXP_LED_OFF;
+	//	ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
+	//	ax->tty->driver.write(ax->tty, 0, &resync_cmd, 1);
+
+
+		/* Start resync timer again -- the TNC might be still absent */
+
+	//	del_timer(&(ax->resync_t));
+	//	ax->resync_t.data = (unsigned long) ax;
+	//	ax->resync_t.function = resync_tnc;
+	//	ax->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
+	//	add_timer(&(ax->resync_t));
+}
+
+
+
+/* identify and execute a standard 6pack command byte */
+
+void decode_std_command(unsigned char cmd, struct ax_disp *ax)
+{
+	unsigned char checksum = 0, channel;
+	unsigned int i;
+
+	channel = cmd & SIXP_CHN_MASK;
+	switch (cmd & SIXP_CMD_MASK) {     /* normal command */
+	case SIXP_SEOF:
+		if ((ax->rx_count == 0) && (ax->rx_count_cooked == 0)) {
+			if ((ax->status & SIXP_RX_DCD_MASK) ==
+				SIXP_RX_DCD_MASK) {
+				ax->led_state = SIXP_CON_LED_ON;
+				//					ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
+			} /* if */
+		}
+		else {
+			ax->led_state = SIXP_LED_OFF;
+			//				ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
+							/* fill trailing bytes with zeroes */
+			if (ax->rx_count == 2) {
+				decode_data(0, ax);
+				decode_data(0, ax);
+				ax->rx_count_cooked -= 2;
+			}
+			else if (ax->rx_count == 3) {
+				decode_data(0, ax);
+				ax->rx_count_cooked -= 1;
+			}
+			for (i = 0; i < ax->rx_count_cooked; i++)
+				checksum += ax->cooked_buf[i];
+			if (checksum != SIXP_CHKSUM) {
+				Debugprintf("6pack: bad checksum %2.2x\n", checksum);
+			}
+			else {
+				ax->rcount = ax->rx_count_cooked - 1;
+				ax_bump(ax);
+			} /* else */
+			ax->rx_count_cooked = 0;
+		} /* else */
+		break;
+	case SIXP_TX_URUN:
+		Debugprintf("6pack: TX underrun\n");
+		break;
+	case SIXP_RX_ORUN:
+		Debugprintf("6pack: RX overrun\n");
+		break;
+	case SIXP_RX_BUF_OVL:
+		Debugprintf("6pack: RX buffer overflow\n");
+	} /* switch */
+} /* function */
+
+/* decode 4 sixpack-encoded bytes into 3 data bytes */
+
+void decode_data(unsigned char inbyte, struct ax_disp *ax)
+{
+	unsigned char *buf;
+
+	if (ax->rx_count != 3)
+		ax->raw_buf[ax->rx_count++] = inbyte;
+	else {
+		buf = ax->raw_buf;
+		ax->cooked_buf[ax->rx_count_cooked++] =
+			buf[0] | ((buf[1] << 2) & 0xc0);
+		ax->cooked_buf[ax->rx_count_cooked++] =
+			(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
+		ax->cooked_buf[ax->rx_count_cooked++] =
+			(buf[2] & 0x03) | (inbyte << 2);
+		ax->rx_count = 0;
+	}
+}
diff --git a/ALSASound.c b/ALSASound.c
index fc950a4..6cb6121 100644
--- a/ALSASound.c
+++ b/ALSASound.c
@@ -1789,32 +1789,6 @@ int gpioInitialise(void)
 
 
 
-int stricmp(const unsigned char * pStr1, const unsigned char *pStr2)
-{
-    unsigned char c1, c2;
-    int  v;
-
-	if (pStr1 == NULL)
-	{
-		if (pStr2)
-			Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2);
-		else
-			Debugprintf("stricmp called with two NULL params");
-
-		return 1;
-	}
-
-
-    do {
-        c1 = *pStr1++;
-        c2 = *pStr2++;
-        /* The casts are necessary when pStr1 is shorter & char is signed */
-        v = tolower(c1) - tolower(c2);
-    } while ((v == 0) && (c1 != '\0') && (c2 != '\0') );
-
-    return v;
-}
-
 char Leds[8]= {0};
 unsigned int PKTLEDTimer = 0;
 
@@ -2107,5 +2081,63 @@ int gethints()
 	return 0;
 }
 
+// Microsoft routines not available in gcc
+
+int memicmp(unsigned char *a, unsigned char *b, int n)
+{
+	if (n)
+	{
+		while (n && (toupper(*a) == toupper(*b)))
+			n--, a++, b++;
+
+		if (n)
+			return toupper(*a) - toupper(*b);
+	}
+	return 0;
+}
+int stricmp(const unsigned char * pStr1, const unsigned char *pStr2)
+{
+	unsigned char c1, c2;
+	int  v;
+
+	if (pStr1 == NULL)
+	{
+		if (pStr2)
+			Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2);
+		else
+			Debugprintf("stricmp called with two NULL params");
+
+		return 1;
+	}
+
+
+	do {
+		c1 = *pStr1++;
+		c2 = *pStr2++;
+		/* The casts are necessary when pStr1 is shorter & char is signed */
+		v = tolower(c1) - tolower(c2);
+	} while ((v == 0) && (c1 != '\0') && (c2 != '\0'));
+
+	return v;
+}
+char * strupr(char* s)
+{
+	char* p = s;
+
+	if (s == 0)
+		return 0;
+
+	while (*p = toupper(*p)) p++;
+	return s;
+}
+
+char * strlwr(char* s)
+{
+	char* p = s;
+	while (*p = tolower(*p)) p++;
+	return s;
+}
+
+
 
 
diff --git a/BusyDetect.c b/BusyDetect.c
index 453bdd2..b0b355a 100644
--- a/BusyDetect.c
+++ b/BusyDetect.c
@@ -45,156 +45,28 @@ VOID ClearBusy()
 	intLastStop = 0;		// This will force the busy detector to ignore old averages and initialze the rolling average filters
 }
 
-/*
-// Function to implement a busy detector based on 1024 point FFT
+extern int FFTSize;
 
-BOOL BusyDetect2(float * dblMag, int intStart, int intStop)        // this only called while searching for leader ...once leader detected, no longer called.
+BOOL BusyDetect3(float * dblMag, int StartFreq, int EndFreq)
 {
-	// each bin is about 12000/1024 or 11.72 Hz
-	// this only called while searching for leader ...once leader detected, no longer called.
+
+	// Based on code from ARDOP, but using diffferent FFT size
+	// QtSM is using an FFT size based on waterfall settings.
+
 	// First sort signals and look at highes signals:baseline ratio..
-
-	float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide;
-	float dblFastAlpha = 0.4f;
-	float dblSlowAlpha = 0.2f;
-	float dblAvgStoNNarrow, dblAvgStoNWide;
-	int intNarrow = 8;  // 8 x 11.72 Hz about 94 z
-	int intWide = ((intStop - intStart) * 2) / 3; //* 0.66);
-	int blnBusy = FALSE;
-	float dblAvgStoNSlowNarrow = 0;
-	float dblAvgStoNFastNarrow = 0;
-	float dblAvgStoNSlowWide = 0;
-	float dblAvgStoNFastWide = 0;
-
-	// First narrow band (~94Hz)
-
-	SortSignals(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow);
-
-	if (intLastStart == intStart && intLastStop == intStop)
-	{
-		dblAvgStoNSlowNarrow = (1 - dblSlowAlpha) * dblAvgStoNSlowNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
-		dblAvgStoNFastNarrow = (1 - dblFastAlpha) * dblAvgStoNFastNarrow + dblFastAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
-	}
-	else
-	{
-		dblAvgStoNSlowNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
-		dblAvgStoNFastNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
-		intLastStart = intStart;
-		intLastStop = intStop;
-	}
 	
-	dblAvgStoNNarrow = max(dblAvgStoNSlowNarrow, dblAvgStoNFastNarrow); // computes fast attack, slow release
-
-	// Wide band (66% ofr current bandwidth) 
-
-	SortSignals(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide);
-
-	if (intLastStart == intStart && intLastStop == intStop)
-	{
-		dblAvgStoNSlowWide = (1 - dblSlowAlpha) * dblAvgStoNSlowWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide;
-		dblAvgStoNFastWide = (1 - dblFastAlpha) * dblAvgStoNFastWide + dblFastAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide;
-	}
-	else
-	{
-		dblAvgStoNSlowWide = dblAVGSignalPerBinWide / dblAVGBaselineWide;
-		dblAvgStoNFastWide = dblAVGSignalPerBinWide / dblAVGBaselineWide;
-		intLastStart = intStart;
-		intLastStop = intStop;
-	}
-
-	dblAvgStoNNarrow = max(dblAvgStoNSlowNarrow, dblAvgStoNFastNarrow); // computes fast attack, slow release
-	dblAvgStoNWide = max(dblAvgStoNSlowWide, dblAvgStoNFastWide); // computes fast attack, slow release
-
-	// Preliminary calibration...future a function of bandwidth and BusyDet.
-   
-	switch (ARQBandwidth)
-	{
-	case B200MAX:
-	case B200FORCED:
-		if (dblAvgStoNNarrow > 1.5 * BusyDet|| dblAvgStoNWide > 2.5 * BusyDet)
-			blnBusy = True;
-		break;
-
-	case B500MAX:
-	case B500FORCED:
-		if (dblAvgStoNNarrow > 1.5 * BusyDet || dblAvgStoNWide > 2.5 * BusyDet)
-			blnBusy = True;
-		break;
-
-	case B1000MAX:
-	case B1000FORCED:
-
-		if (dblAvgStoNNarrow > 1.4 * BusyDet || dblAvgStoNWide > 2 * BusyDet)
-			blnBusy = True;
-		break;
-
-	case B2000MAX:
-	case B2000FORCED:
-		if (dblAvgStoNNarrow > 1.4 * BusyDet || dblAvgStoNWide > 2 * BusyDet)
-			blnBusy = True;
-	}
-
-	if (blnBusy) // This used to skip over one call busy nuisance trips. Busy must be present at least 2 consecutive times to be reported
-	{
-		intBusyOnCnt += 1;
-		intBusyOffCnt = 0;
-        if (intBusyOnCnt > 1)
-			blnBusy = True;
-		else if (!blnBusy)
-		{
-			intBusyOffCnt += 1;
-			intBusyOnCnt = 0;
-			if (intBusyOffCnt > 3)
-				blnBusy = False;
-		}
-	}
-	if (blnLastBusy == False && blnBusy)
-	{
-		int x = round(dblAvgStoNNarrow);	// odd, but PI doesnt print floats properly 
-		int y = round(dblAvgStoNWide);
-		
-		blnLastBusy = True;
-		LastBusyOn = Now;
-#ifdef __ARM_ARCH
-		WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY ON  StoN Narrow = %d StoN Wide %d", x, y);
-#else
-		WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY ON  StoN Narrow = %f StoN Wide %f", dblAvgStoNNarrow, dblAvgStoNWide);
-#endif
-	}
-	else if (blnLastBusy == True && !blnBusy)
-	{
-		int x = round(dblAvgStoNNarrow);	// odd, but PI doesnt print floats properly 
-		int y = round(dblAvgStoNWide);
-
-		blnLastBusy = False;
-		LastBusyOff = Now;
-#ifdef __ARM_ARCH
-		WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY OFF StoN Narrow = %d StoN Wide %d", x, y);
-#else
-		WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY OFF StoN Narrow = %f StoN Wide %f", dblAvgStoNNarrow, dblAvgStoNWide);
-#endif
-	}
-
-	return blnLastBusy;
-}
+	// Start and Stop are in Hz. Convert to bin numbers
 
 
-*/
-
-
-
-
-BOOL BusyDetect3(float * dblMag, int intStart, int intStop)        // this only called while searching for leader ...once leader detected, no longer called.
-{
-	// each bin is about 12000/1024 or 11.72 Hz
-	// this only called while searching for leader ...once leader detected, no longer called.
-	// First sort signals and look at highes signals:baseline ratio..
+	float BinSize = 12000.0 / FFTSize;
+	int StartBin = StartFreq / BinSize;
+	int EndBin = EndFreq / BinSize;
 
 	float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide;
 	float dblSlowAlpha = 0.2f;
 	float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0;
-	int intNarrow = 8;  // 8 x 11.72 Hz about 94 z
-	int intWide = ((intStop - intStart) * 2) / 3; //* 0.66);
+	int intNarrow = 100 / BinSize;  // 8 x 11.72 Hz about 94 z
+	int intWide = ((EndBin - StartBin) * 2) / 3; //* 0.66);
 	int blnBusy = FALSE;
 	int  BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet;
 
@@ -202,32 +74,32 @@ BOOL BusyDetect3(float * dblMag, int intStart, int intStop)        // this only
 	// First sort signals and look at highest signals:baseline ratio..
 	// First narrow band (~94Hz)
 
-	SortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow);
+	SortSignals2(dblMag, StartBin, EndBin, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow);
 
-	if (intLastStart == intStart && intLastStop == intStop)
+	if (intLastStart == StartBin && intLastStop == EndBin)
 		dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
 	else
 	{
 		// This initializes the Narrow average after a bandwidth change
 
 		dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
- 		intLastStart = intStart;
-		intLastStop = intStop;
+ 		intLastStart = StartBin;
+		intLastStop = EndBin;
 	}
 	
 	// Wide band (66% of current bandwidth)
 	
-	SortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide);
+	SortSignals2(dblMag, StartBin, EndBin, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide);
 
-	if (intLastStart == intStart && intLastStop == intStop)
+	if (intLastStart == StartBin && intLastStop == EndBin)
 		dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide;
 	else
 	{
 		// This initializes the Wide average after a bandwidth change
 		
 		dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide;
-		intLastStart = intStart;
-		intLastStop = intStop;
+		intLastStart = StartBin;
+		intLastStop = EndBin;
 	}
 
 	// Preliminary calibration...future a function of bandwidth and BusyDet.
diff --git a/Config.cpp b/Config.cpp
index e2239f9..09513a9 100644
--- a/Config.cpp
+++ b/Config.cpp
@@ -29,7 +29,7 @@ extern "C" void get_exclude_list(char * line, TStringList * list);
 extern "C" void get_exclude_frm(char * line, TStringList * list);
 
 extern "C" int SoundMode; 
-extern "C" int onlyMixSnoop;
+extern "C" bool onlyMixSnoop;
 
 //extern "C" int RX_SR;
 //extern "C" int TX_SR;
@@ -59,6 +59,7 @@ extern int CWIDRight;
 extern int CWIDType;
 extern bool afterTraffic;
 extern bool darkTheme;
+extern "C" bool useKISSControls;
 
 extern "C" int RSID_SABM[4];
 extern "C" int RSID_UI[4];
@@ -66,8 +67,10 @@ extern "C" int RSID_SetModem[4];
 
 extern "C" int nonGUIMode;
 
-
-
+extern char SixPackDevice[256];
+extern int SixPackPort;
+extern int SixPackEnable;
+extern int MgmtPort;
 extern QFont Font;
 
 
@@ -216,6 +219,7 @@ void getSettings()
 	soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt();
 
 	SCO = settings->value("Init/SCO", 0).toInt();
+	useKISSControls = settings->value("Init/useKISSControls", 0).toBool();
 
 	dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt();
 	rxOffset = settings->value("Modem/rxOffset", 0).toInt();
@@ -224,6 +228,11 @@ void getSettings()
 	AGWPort = settings->value("AGWHost/Port", 8000).toInt();
 	KISSServ = settings->value("KISS/Server", FALSE).toBool();
 	KISSPort = settings->value("KISS/Port", 8105).toInt();
+	MgmtPort = settings->value("MGMT/Port", 0).toInt();
+
+	SixPackEnable = settings->value("SixPack/Enable", FALSE).toBool();
+	SixPackPort = settings->value("SixPack/Port", 0).toInt();
+	strcpy(SixPackDevice, settings->value("SixPack/Device", "").toString().toUtf8());
 
 //	RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM;
 //	TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM;
@@ -402,6 +411,7 @@ void saveSettings()
 	settings->setValue("Init/SndRXDeviceName", CaptureDevice);
 	settings->setValue("Init/SndTXDeviceName", PlaybackDevice);
 
+	settings->setValue("Init/useKISSControls", useKISSControls);
 	settings->setValue("Init/SCO", SCO);
 	settings->setValue("Init/DualPTT", DualPTT);
 	settings->setValue("Init/TXRotate", TX_rotate);
@@ -460,6 +470,12 @@ void saveSettings()
 	settings->setValue("AGWHost/Port", AGWPort);
 	settings->setValue("KISS/Server", KISSServ);
 	settings->setValue("KISS/Port", KISSPort);
+	settings->setValue("MGMT/Port", MgmtPort);
+
+
+	settings->setValue("SixPack/Enable", SixPackEnable);
+	settings->setValue("SixPack/Port", SixPackPort);
+	settings->setValue("SixPack/Device", SixPackDevice);
 
 	settings->setValue("Modem/PreEmphasisAll1", emph_all[0]);
 	settings->setValue("Modem/PreEmphasisAll2", emph_all[1]);
diff --git a/Config.cpp.bak b/Config.cpp.bak
index d5d7a67..f660aaa 100644
--- a/Config.cpp.bak
+++ b/Config.cpp.bak
@@ -1,481 +1,481 @@
-/*extern "C" 
-Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
-
-This file is part of QtSoundModem
-
-QtSoundModem is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-QtSoundModem is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with QtSoundModem.  If not, see http://www.gnu.org/licenses
-
-*/
-
-// UZ7HO Soundmodem Port by John Wiseman G8BPQ
-
-#include <QSettings>
-#include <QDialog>
-
-#include "UZ7HOStuff.h"
-
-extern "C" void get_exclude_list(char * line, TStringList * list);
-extern "C" void get_exclude_frm(char * line, TStringList * list);
-
-extern "C" int SoundMode; 
-extern "C" int RX_SR;
-extern "C" int TX_SR;
-extern "C" int multiCore;
-extern "C" char * Wisdom;
-extern int WaterfallMin;
-extern int WaterfallMax;
-
-extern "C" word MEMRecovery[5];
-
-extern int MintoTray;
-extern "C" int UDPClientPort;
-extern "C" int UDPServerPort;
-extern "C" int TXPort;
-
-extern char UDPHost[64];
-extern QDialog * constellationDialog;
-extern QRect PSKRect;
-
-extern char CWIDCall[128];
-extern "C" char CWIDMark[32];
-extern int CWIDInterval;
-extern int CWIDLeft;
-extern int CWIDRight;
-extern int CWIDType;
-extern bool afterTraffic;
-extern bool darkTheme;
-
-extern "C" int RSID_SABM[4];
-extern "C" int RSID_UI[4];
-extern "C" int RSID_SetModem[4];
-
-extern QFont Font;
-
-
-QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-
-// This makes geting settings for more channels easier
-
-char Prefix[16] = "AX25_A";
-
-void GetPortSettings(int Chan);
-
-QVariant getAX25Param(const char * key, QVariant Default)
-{
-	char fullKey[64];
-	QVariant Q;
-	QByteArray x;
-	sprintf(fullKey, "%s/%s", Prefix, key);
-	Q = settings->value(fullKey, Default);
-	x = Q.toString().toUtf8();
-
-	return Q;
-}
-
-void getAX25Params(int chan)
-{
-	Prefix[5] = chan + 'A';
-	GetPortSettings(chan);
-}
-
-
-void GetPortSettings(int Chan)
-{
-	tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt();
-
-	maxframe[Chan] = getAX25Param("Maxframe", 3).toInt();
-	fracks[Chan] = getAX25Param("Retries", 15).toInt();
-	frack_time[Chan] = getAX25Param("FrackTime", 5).toInt();
-
-	idletime[Chan] = getAX25Param("IdleTime", 180).toInt();
-	slottime[Chan] = getAX25Param("SlotTime", 100).toInt();
-	persist[Chan] = getAX25Param("Persist", 128).toInt();
-	resptime[Chan] = getAX25Param("RespTime", 1500).toInt();
-	TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt();
-	max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt();
-	KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();;
-	dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();;
-	recovery[Chan] = getAX25Param("BitRecovery", 0).toInt();
-	NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();;
-	MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt();
-	IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt();
-
-	strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8());
-	strcpy(exclude_callsigns[Chan], getAX25Param("ExcludeCallsigns", "").toString().toUtf8());
-
-	fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt();
-	il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt();
-	il2p_crc[Chan] = getAX25Param("IL2PCRC", 0).toInt();
-	RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt();
-	RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt();
-	RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt();
-}
-
-void getSettings()
-{
-	int snd_ch;
-
-	QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-	settings->sync();
-
-	PSKRect = settings->value("PSKWindow").toRect();
-
-	SoundMode = settings->value("Init/SoundMode", 0).toInt();
-	UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt();
-	UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt();
-	TXPort = settings->value("Init/TXPort", UDPServerPort).toInt();
-
-	strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8());
-	UDPServ = settings->value("Init/UDPServer", FALSE).toBool();
-
-	RX_SR = settings->value("Init/RXSampleRate", 12000).toInt();
-	TX_SR = settings->value("Init/TXSampleRate", 12000).toInt();
-
-	strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8());
-	strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8());
-
-	raduga = settings->value("Init/DispMode", DISP_RGB).toInt();
-
-	strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8());
-	PTTMode = settings->value("Init/PTTMode", 19200).toInt();
-	PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt();
-
-	strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8());
-	strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8());
-
-	pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt();
-	pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt();
-
-#ifdef WIN32
-	strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8());
-#else
-	strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8());
-#endif
-
-	HamLibPort = settings->value("Init/HamLibPort", 4532).toInt();
-	strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8());
-
-	DualPTT = settings->value("Init/DualPTT", 1).toInt();
-	TX_rotate = settings->value("Init/TXRotate", 0).toInt();
-	multiCore = settings->value("Init/multiCore", 0).toInt();
-	MintoTray = settings->value("Init/MinimizetoTray", 1).toInt();
-	Wisdom = strdup(settings->value("Init/Wisdom", "").toString().toUtf8());
-	WaterfallMin = settings->value("Init/WaterfallMin", 0).toInt();
-	WaterfallMax = settings->value("Init/WaterfallMax", 3300).toInt();
-
-
-	rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt();
-	rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt();
-	rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt();
-	rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt();
-
-	rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt();
-	rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt();
-	rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt();
-	rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt();
-	speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt();
-	speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt();
-	speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt();
-	speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt();
-
-	RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();;
-	RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();;
-	RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();;
-	RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();;
-
-	soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt();
-	soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt();
-	soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt();
-	soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt();
-
-	SCO = settings->value("Init/SCO", 0).toInt();
-
-	dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt();
-	rxOffset = settings->value("Modem/rxOffset", 0).toInt();
-
-	AGWServ = settings->value("AGWHost/Server", TRUE).toBool();
-	AGWPort = settings->value("AGWHost/Port", 8000).toInt();
-	KISSServ = settings->value("KISS/Server", FALSE).toBool();
-	KISSPort = settings->value("KISS/Port", 8105).toInt();
-
-	RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM;
-	TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM;
-
-	emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool();
-	emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool();
-	emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool();
-	emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool();
-
-	emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt();
-	emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt();
-	emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt();
-	emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt();
-
-	Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt();
-	Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt();
-
-	txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt();
-	txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt();
-	txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt();
-	txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt();
-
-	txtail[0] = settings->value("Modem/TxTail1", 50).toInt();
-	txtail[1] = settings->value("Modem/TxTail2", 50).toInt();
-	txtail[2] = settings->value("Modem/TxTail3", 50).toInt();
-	txtail[3] = settings->value("Modem/TxTail4", 50).toInt();
-
-	strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper());
-	strcpy(CWIDMark, settings->value("Modem/CWIDMark", "").toString().toUtf8().toUpper());
-	CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt();
-	CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt();
-	CWIDRight = settings->value("Modem/CWIDRight", 0).toInt();
-	CWIDType = settings->value("Modem/CWIDType", 1).toInt();			// on/off
-	afterTraffic = settings->value("Modem/afterTraffic", false).toBool();
-
-	getAX25Params(0);
-	getAX25Params(1);
-	getAX25Params(2);
-	getAX25Params(3);
-
-	// Validate and process settings
-
-	UsingLeft = 0;
-	UsingRight = 0;
-	UsingBothChannels = 0;
-
-	for (int i = 0; i < 4; i++)
-	{
-		if (soundChannel[i] == LEFT)
-		{
-			UsingLeft = 1;
-			modemtoSoundLR[i] = 0;
-		}
-		else if (soundChannel[i] == RIGHT)
-		{
-			UsingRight = 1;
-			modemtoSoundLR[i] = 1;
-		}
-	}
-
-	if (UsingLeft && UsingRight)
-		UsingBothChannels = 1;
-
-	for (snd_ch = 0; snd_ch < 4; snd_ch++)
-	{
-		tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f);
-
-		if (IPOLL[snd_ch] < 0)
-			IPOLL[snd_ch] = 0;
-		else if (IPOLL[snd_ch] > 65535)
-			IPOLL[snd_ch] = 65535;
-
-		if (MEMRecovery[snd_ch] < 1)
-			MEMRecovery[snd_ch] = 1;
-
-		//			if (MEMRecovery[snd_ch]> 65535)
-		//				MEMRecovery[snd_ch]= 65535;
-
-				/*
-				if resptime[snd_ch] < 0 then resptime[snd_ch]= 0;
-					if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535;
-					if persist[snd_ch] > 255 then persist[snd_ch]= 255;
-					if persist[snd_ch] < 32 then persist[snd_ch]= 32;
-					if fracks[snd_ch] < 1 then fracks[snd_ch]= 1;
-					if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1;
-					if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180;
-				*/
-
-		if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph)
-			emph_db[snd_ch] = 0;
-
-		if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6;
-		if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3;
-		if (qpsk_set[snd_ch].mode > 1)  qpsk_set[snd_ch].mode = 0;
-
-	}
-
-	darkTheme = settings->value("Init/darkTheme", false).toBool();
-
-	delete(settings);
-}
-
-void SavePortSettings(int Chan);
-
-void saveAX25Param(const char * key, QVariant Value)
-{
-	char fullKey[64];
-
-	sprintf(fullKey, "%s/%s", Prefix, key);
-
-	settings->setValue(fullKey, Value);
-}
-
-void saveAX25Params(int chan)
-{
-	Prefix[5] = chan + 'A';
-	SavePortSettings(chan);
-}
-
-void SavePortSettings(int Chan)
-{
-	saveAX25Param("Retries", fracks[Chan]);
-	saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]);
-	saveAX25Param("Maxframe",maxframe[Chan]);
-	saveAX25Param("Retries", fracks[Chan]);
-	saveAX25Param("FrackTime", frack_time[Chan]);
-	saveAX25Param("IdleTime", idletime[Chan]);
-	saveAX25Param("SlotTime", slottime[Chan]);
-	saveAX25Param("Persist", persist[Chan]);
-	saveAX25Param("RespTime", resptime[Chan]);
-	saveAX25Param("TXFrmMode", TXFrmMode[Chan]);
-	saveAX25Param("FrameCollector", max_frame_collector[Chan]);
-	saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]);
-	saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]);
-	saveAX25Param("KISSOptimization", KISS_opt[Chan]);
-	saveAX25Param("DynamicFrack", dyn_frack[Chan]);
-	saveAX25Param("BitRecovery", recovery[Chan]);
-	saveAX25Param("NonAX25Frm", NonAX25[Chan]);
-	saveAX25Param("MEMRecovery", MEMRecovery[Chan]);
-	saveAX25Param("IPOLL", IPOLL[Chan]);
-	saveAX25Param("MyDigiCall", MyDigiCall[Chan]);
-	saveAX25Param("FX25", fx25_mode[Chan]);
-	saveAX25Param("IL2P", il2p_mode[Chan]);
-	saveAX25Param("IL2PCRC", il2p_crc[Chan]);
-	saveAX25Param("RSID_UI", RSID_UI[Chan]);
-	saveAX25Param("RSID_SABM", RSID_SABM[Chan]);
-	saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]);
-}
-
-void saveSettings()
-{
-	QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-
-	settings->setValue("FontFamily", Font.family());
-	settings->setValue("PointSize", Font.pointSize());
-	settings->setValue("Weight", Font.weight());
-
-	settings->setValue("PSKWindow", constellationDialog->geometry());
-	settings->setValue("Init/SoundMode", SoundMode);
-	settings->setValue("Init/UDPClientPort", UDPClientPort);
-	settings->setValue("Init/UDPServerPort", UDPServerPort);
-	settings->setValue("Init/TXPort", TXPort);
-
-	settings->setValue("Init/UDPServer", UDPServ);
-	settings->setValue("Init/UDPHost", UDPHost);
-
-
-	settings->setValue("Init/TXSampleRate", TX_SR);
-	settings->setValue("Init/RXSampleRate", RX_SR);
-
-	settings->setValue("Init/SndRXDeviceName", CaptureDevice);
-	settings->setValue("Init/SndTXDeviceName", PlaybackDevice);
-
-	settings->setValue("Init/SCO", SCO);
-	settings->setValue("Init/DualPTT", DualPTT);
-	settings->setValue("Init/TXRotate", TX_rotate);
-
-	settings->setValue("Init/DispMode", raduga);
-
-	settings->setValue("Init/PTT", PTTPort);
-	settings->setValue("Init/PTTBAUD", PTTBAUD);
-	settings->setValue("Init/PTTMode", PTTMode);
-
-	settings->setValue("Init/PTTOffString", PTTOffString);
-	settings->setValue("Init/PTTOnString", PTTOnString);
-
-	settings->setValue("Init/pttGPIOPin", pttGPIOPin);
-	settings->setValue("Init/pttGPIOPinR", pttGPIOPinR);
-
-	settings->setValue("Init/CM108Addr", CM108Addr);
-	settings->setValue("Init/HamLibPort", HamLibPort);
-	settings->setValue("Init/HamLibHost", HamLibHost);
-	settings->setValue("Init/MinimizetoTray", MintoTray);
-	settings->setValue("Init/multiCore", multiCore);
-	settings->setValue("Init/Wisdom", Wisdom);
-
-	settings->setValue("Init/WaterfallMin", WaterfallMin);
-	settings->setValue("Init/WaterfallMax", WaterfallMax);
-
-	// Don't save freq on close as it could be offset by multiple decoders
-
-	settings->setValue("Modem/NRRcvrPairs1", RCVR[0]);
-	settings->setValue("Modem/NRRcvrPairs2", RCVR[1]);
-	settings->setValue("Modem/NRRcvrPairs3", RCVR[2]);
-	settings->setValue("Modem/NRRcvrPairs4", RCVR[3]);
-
-	settings->setValue("Modem/RcvrShift1", rcvr_offset[0]);
-	settings->setValue("Modem/RcvrShift2", rcvr_offset[1]);
-	settings->setValue("Modem/RcvrShift3", rcvr_offset[2]);
-	settings->setValue("Modem/RcvrShift4", rcvr_offset[3]);
-
-	settings->setValue("Modem/ModemType1", speed[0]);
-	settings->setValue("Modem/ModemType2", speed[1]);
-	settings->setValue("Modem/ModemType3", speed[2]);
-	settings->setValue("Modem/ModemType4", speed[3]);
-
-	settings->setValue("Modem/soundChannel1", soundChannel[0]);
-	settings->setValue("Modem/soundChannel2", soundChannel[1]);
-	settings->setValue("Modem/soundChannel3", soundChannel[2]);
-	settings->setValue("Modem/soundChannel4", soundChannel[3]);
-
-	settings->setValue("Modem/DCDThreshold", dcd_threshold);
-	settings->setValue("Modem/rxOffset", rxOffset);
-
-	settings->setValue("AGWHost/Server", AGWServ);
-	settings->setValue("AGWHost/Port", AGWPort);
-	settings->setValue("KISS/Server", KISSServ);
-	settings->setValue("KISS/Port", KISSPort);
-
-	settings->setValue("Modem/PreEmphasisAll1", emph_all[0]);
-	settings->setValue("Modem/PreEmphasisAll2", emph_all[1]);
-	settings->setValue("Modem/PreEmphasisAll3", emph_all[2]);
-	settings->setValue("Modem/PreEmphasisAll4", emph_all[3]);
-
-	settings->setValue("Modem/PreEmphasisDB1", emph_db[0]);
-	settings->setValue("Modem/PreEmphasisDB2", emph_db[1]);
-	settings->setValue("Modem/PreEmphasisDB3", emph_db[2]);
-	settings->setValue("Modem/PreEmphasisDB4", emph_db[3]);
-
-	settings->setValue("Window/Waterfall1", Firstwaterfall);
-	settings->setValue("Window/Waterfall2", Secondwaterfall);
-
-	settings->setValue("Modem/TxDelay1", txdelay[0]);
-	settings->setValue("Modem/TxDelay2", txdelay[1]);
-	settings->setValue("Modem/TxDelay3", txdelay[2]);
-	settings->setValue("Modem/TxDelay4", txdelay[3]);
-
-	settings->setValue("Modem/TxTail1", txtail[0]);
-	settings->setValue("Modem/TxTail2", txtail[1]);
-	settings->setValue("Modem/TxTail3", txtail[2]);
-	settings->setValue("Modem/TxTail4", txtail[3]);
-
-	settings->setValue("Modem/CWIDCall", CWIDCall);
-	settings->setValue("Modem/CWIDMark", CWIDMark);
-	settings->setValue("Modem/CWIDInterval", CWIDInterval);
-	settings->setValue("Modem/CWIDLeft", CWIDLeft);
-	settings->setValue("Modem/CWIDRight", CWIDRight);
-	settings->setValue("Modem/CWIDType", CWIDType);
-	settings->setValue("Modem/afterTraffic", afterTraffic);
-
-	settings->setValue("Init/darkTheme", darkTheme);
-
-	saveAX25Params(0);
-	saveAX25Params(1);
-	saveAX25Params(2);
-	saveAX25Params(3);
-
-	settings->sync();
-
-	delete(settings);
-}
+/*extern "C" 
+Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
+
+This file is part of QtSoundModem
+
+QtSoundModem is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QtSoundModem is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with QtSoundModem.  If not, see http://www.gnu.org/licenses
+
+*/
+
+// UZ7HO Soundmodem Port by John Wiseman G8BPQ
+
+#include <QSettings>
+#include <QDialog>
+
+#include "UZ7HOStuff.h"
+
+extern "C" void get_exclude_list(char * line, TStringList * list);
+extern "C" void get_exclude_frm(char * line, TStringList * list);
+
+extern "C" int SoundMode; 
+extern "C" int RX_SR;
+extern "C" int TX_SR;
+extern "C" int multiCore;
+extern "C" char * Wisdom;
+extern int WaterfallMin;
+extern int WaterfallMax;
+
+extern "C" word MEMRecovery[5];
+
+extern int MintoTray;
+extern "C" int UDPClientPort;
+extern "C" int UDPServerPort;
+extern "C" int TXPort;
+
+extern char UDPHost[64];
+extern QDialog * constellationDialog;
+extern QRect PSKRect;
+
+extern char CWIDCall[128];
+extern "C" char CWIDMark[32];
+extern int CWIDInterval;
+extern int CWIDLeft;
+extern int CWIDRight;
+extern int CWIDType;
+extern bool afterTraffic;
+extern bool darkTheme;
+
+extern "C" int RSID_SABM[4];
+extern "C" int RSID_UI[4];
+extern "C" int RSID_SetModem[4];
+
+extern QFont Font;
+
+
+QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+
+// This makes geting settings for more channels easier
+
+char Prefix[16] = "AX25_A";
+
+void GetPortSettings(int Chan);
+
+QVariant getAX25Param(const char * key, QVariant Default)
+{
+	char fullKey[64];
+	QVariant Q;
+	QByteArray x;
+	sprintf(fullKey, "%s/%s", Prefix, key);
+	Q = settings->value(fullKey, Default);
+	x = Q.toString().toUtf8();
+
+	return Q;
+}
+
+void getAX25Params(int chan)
+{
+	Prefix[5] = chan + 'A';
+	GetPortSettings(chan);
+}
+
+
+void GetPortSettings(int Chan)
+{
+	tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt();
+
+	maxframe[Chan] = getAX25Param("Maxframe", 3).toInt();
+	fracks[Chan] = getAX25Param("Retries", 15).toInt();
+	frack_time[Chan] = getAX25Param("FrackTime", 5).toInt();
+
+	idletime[Chan] = getAX25Param("IdleTime", 180).toInt();
+	slottime[Chan] = getAX25Param("SlotTime", 100).toInt();
+	persist[Chan] = getAX25Param("Persist", 128).toInt();
+	resptime[Chan] = getAX25Param("RespTime", 1500).toInt();
+	TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt();
+	max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt();
+	KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();;
+	dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();;
+	recovery[Chan] = getAX25Param("BitRecovery", 0).toInt();
+	NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();;
+	MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt();
+	IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt();
+
+	strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8());
+	strcpy(exclude_callsigns[Chan], getAX25Param("ExcludeCallsigns", "").toString().toUtf8());
+
+	fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt();
+	il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt();
+	il2p_crc[Chan] = getAX25Param("IL2PCRC", 0).toInt();
+	RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt();
+	RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt();
+	RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt();
+}
+
+void getSettings()
+{
+	int snd_ch;
+
+	QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+	settings->sync();
+
+	PSKRect = settings->value("PSKWindow").toRect();
+
+	SoundMode = settings->value("Init/SoundMode", 0).toInt();
+	UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt();
+	UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt();
+	TXPort = settings->value("Init/TXPort", UDPServerPort).toInt();
+
+	strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8());
+	UDPServ = settings->value("Init/UDPServer", FALSE).toBool();
+
+	RX_SR = settings->value("Init/RXSampleRate", 12000).toInt();
+	TX_SR = settings->value("Init/TXSampleRate", 12000).toInt();
+
+	strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8());
+	strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8());
+
+	raduga = settings->value("Init/DispMode", DISP_RGB).toInt();
+
+	strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8());
+	PTTMode = settings->value("Init/PTTMode", 19200).toInt();
+	PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt();
+
+	strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8());
+	strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8());
+
+	pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt();
+	pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt();
+
+#ifdef WIN32
+	strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8());
+#else
+	strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8());
+#endif
+
+	HamLibPort = settings->value("Init/HamLibPort", 4532).toInt();
+	strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8());
+
+	DualPTT = settings->value("Init/DualPTT", 1).toInt();
+	TX_rotate = settings->value("Init/TXRotate", 0).toInt();
+	multiCore = settings->value("Init/multiCore", 0).toInt();
+	MintoTray = settings->value("Init/MinimizetoTray", 1).toInt();
+	Wisdom = strdup(settings->value("Init/Wisdom", "").toString().toUtf8());
+	WaterfallMin = settings->value("Init/WaterfallMin", 0).toInt();
+	WaterfallMax = settings->value("Init/WaterfallMax", 3300).toInt();
+
+
+	rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt();
+	rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt();
+	rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt();
+	rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt();
+
+	rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt();
+	rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt();
+	rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt();
+	rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt();
+	speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt();
+	speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt();
+	speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt();
+	speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt();
+
+	RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();;
+	RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();;
+	RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();;
+	RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();;
+
+	soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt();
+	soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt();
+	soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt();
+	soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt();
+
+	SCO = settings->value("Init/SCO", 0).toInt();
+
+	dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt();
+	rxOffset = settings->value("Modem/rxOffset", 0).toInt();
+
+	AGWServ = settings->value("AGWHost/Server", TRUE).toBool();
+	AGWPort = settings->value("AGWHost/Port", 8000).toInt();
+	KISSServ = settings->value("KISS/Server", FALSE).toBool();
+	KISSPort = settings->value("KISS/Port", 8105).toInt();
+
+	RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM;
+	TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM;
+
+	emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool();
+	emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool();
+	emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool();
+	emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool();
+
+	emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt();
+	emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt();
+	emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt();
+	emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt();
+
+	Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt();
+	Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt();
+
+	txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt();
+	txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt();
+	txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt();
+	txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt();
+
+	txtail[0] = settings->value("Modem/TxTail1", 50).toInt();
+	txtail[1] = settings->value("Modem/TxTail2", 50).toInt();
+	txtail[2] = settings->value("Modem/TxTail3", 50).toInt();
+	txtail[3] = settings->value("Modem/TxTail4", 50).toInt();
+
+	strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper());
+	strcpy(CWIDMark, settings->value("Modem/CWIDMark", "").toString().toUtf8().toUpper());
+	CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt();
+	CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt();
+	CWIDRight = settings->value("Modem/CWIDRight", 0).toInt();
+	CWIDType = settings->value("Modem/CWIDType", 1).toInt();			// on/off
+	afterTraffic = settings->value("Modem/afterTraffic", false).toBool();
+
+	getAX25Params(0);
+	getAX25Params(1);
+	getAX25Params(2);
+	getAX25Params(3);
+
+	// Validate and process settings
+
+	UsingLeft = 0;
+	UsingRight = 0;
+	UsingBothChannels = 0;
+
+	for (int i = 0; i < 4; i++)
+	{
+		if (soundChannel[i] == LEFT)
+		{
+			UsingLeft = 1;
+			modemtoSoundLR[i] = 0;
+		}
+		else if (soundChannel[i] == RIGHT)
+		{
+			UsingRight = 1;
+			modemtoSoundLR[i] = 1;
+		}
+	}
+
+	if (UsingLeft && UsingRight)
+		UsingBothChannels = 1;
+
+	for (snd_ch = 0; snd_ch < 4; snd_ch++)
+	{
+		tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f);
+
+		if (IPOLL[snd_ch] < 0)
+			IPOLL[snd_ch] = 0;
+		else if (IPOLL[snd_ch] > 65535)
+			IPOLL[snd_ch] = 65535;
+
+		if (MEMRecovery[snd_ch] < 1)
+			MEMRecovery[snd_ch] = 1;
+
+		//			if (MEMRecovery[snd_ch]> 65535)
+		//				MEMRecovery[snd_ch]= 65535;
+
+				/*
+				if resptime[snd_ch] < 0 then resptime[snd_ch]= 0;
+					if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535;
+					if persist[snd_ch] > 255 then persist[snd_ch]= 255;
+					if persist[snd_ch] < 32 then persist[snd_ch]= 32;
+					if fracks[snd_ch] < 1 then fracks[snd_ch]= 1;
+					if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1;
+					if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180;
+				*/
+
+		if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph)
+			emph_db[snd_ch] = 0;
+
+		if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6;
+		if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3;
+		if (qpsk_set[snd_ch].mode > 1)  qpsk_set[snd_ch].mode = 0;
+
+	}
+
+	darkTheme = settings->value("Init/darkTheme", false).toBool();
+
+	delete(settings);
+}
+
+void SavePortSettings(int Chan);
+
+void saveAX25Param(const char * key, QVariant Value)
+{
+	char fullKey[64];
+
+	sprintf(fullKey, "%s/%s", Prefix, key);
+
+	settings->setValue(fullKey, Value);
+}
+
+void saveAX25Params(int chan)
+{
+	Prefix[5] = chan + 'A';
+	SavePortSettings(chan);
+}
+
+void SavePortSettings(int Chan)
+{
+	saveAX25Param("Retries", fracks[Chan]);
+	saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]);
+	saveAX25Param("Maxframe",maxframe[Chan]);
+	saveAX25Param("Retries", fracks[Chan]);
+	saveAX25Param("FrackTime", frack_time[Chan]);
+	saveAX25Param("IdleTime", idletime[Chan]);
+	saveAX25Param("SlotTime", slottime[Chan]);
+	saveAX25Param("Persist", persist[Chan]);
+	saveAX25Param("RespTime", resptime[Chan]);
+	saveAX25Param("TXFrmMode", TXFrmMode[Chan]);
+	saveAX25Param("FrameCollector", max_frame_collector[Chan]);
+	saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]);
+	saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]);
+	saveAX25Param("KISSOptimization", KISS_opt[Chan]);
+	saveAX25Param("DynamicFrack", dyn_frack[Chan]);
+	saveAX25Param("BitRecovery", recovery[Chan]);
+	saveAX25Param("NonAX25Frm", NonAX25[Chan]);
+	saveAX25Param("MEMRecovery", MEMRecovery[Chan]);
+	saveAX25Param("IPOLL", IPOLL[Chan]);
+	saveAX25Param("MyDigiCall", MyDigiCall[Chan]);
+	saveAX25Param("FX25", fx25_mode[Chan]);
+	saveAX25Param("IL2P", il2p_mode[Chan]);
+	saveAX25Param("IL2PCRC", il2p_crc[Chan]);
+	saveAX25Param("RSID_UI", RSID_UI[Chan]);
+	saveAX25Param("RSID_SABM", RSID_SABM[Chan]);
+	saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]);
+}
+
+void saveSettings()
+{
+	QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+
+	settings->setValue("FontFamily", Font.family());
+	settings->setValue("PointSize", Font.pointSize());
+	settings->setValue("Weight", Font.weight());
+
+	settings->setValue("PSKWindow", constellationDialog->geometry());
+	settings->setValue("Init/SoundMode", SoundMode);
+	settings->setValue("Init/UDPClientPort", UDPClientPort);
+	settings->setValue("Init/UDPServerPort", UDPServerPort);
+	settings->setValue("Init/TXPort", TXPort);
+
+	settings->setValue("Init/UDPServer", UDPServ);
+	settings->setValue("Init/UDPHost", UDPHost);
+
+
+	settings->setValue("Init/TXSampleRate", TX_SR);
+	settings->setValue("Init/RXSampleRate", RX_SR);
+
+	settings->setValue("Init/SndRXDeviceName", CaptureDevice);
+	settings->setValue("Init/SndTXDeviceName", PlaybackDevice);
+
+	settings->setValue("Init/SCO", SCO);
+	settings->setValue("Init/DualPTT", DualPTT);
+	settings->setValue("Init/TXRotate", TX_rotate);
+
+	settings->setValue("Init/DispMode", raduga);
+
+	settings->setValue("Init/PTT", PTTPort);
+	settings->setValue("Init/PTTBAUD", PTTBAUD);
+	settings->setValue("Init/PTTMode", PTTMode);
+
+	settings->setValue("Init/PTTOffString", PTTOffString);
+	settings->setValue("Init/PTTOnString", PTTOnString);
+
+	settings->setValue("Init/pttGPIOPin", pttGPIOPin);
+	settings->setValue("Init/pttGPIOPinR", pttGPIOPinR);
+
+	settings->setValue("Init/CM108Addr", CM108Addr);
+	settings->setValue("Init/HamLibPort", HamLibPort);
+	settings->setValue("Init/HamLibHost", HamLibHost);
+	settings->setValue("Init/MinimizetoTray", MintoTray);
+	settings->setValue("Init/multiCore", multiCore);
+	settings->setValue("Init/Wisdom", Wisdom);
+
+	settings->setValue("Init/WaterfallMin", WaterfallMin);
+	settings->setValue("Init/WaterfallMax", WaterfallMax);
+
+	// Don't save freq on close as it could be offset by multiple decoders
+
+	settings->setValue("Modem/NRRcvrPairs1", RCVR[0]);
+	settings->setValue("Modem/NRRcvrPairs2", RCVR[1]);
+	settings->setValue("Modem/NRRcvrPairs3", RCVR[2]);
+	settings->setValue("Modem/NRRcvrPairs4", RCVR[3]);
+
+	settings->setValue("Modem/RcvrShift1", rcvr_offset[0]);
+	settings->setValue("Modem/RcvrShift2", rcvr_offset[1]);
+	settings->setValue("Modem/RcvrShift3", rcvr_offset[2]);
+	settings->setValue("Modem/RcvrShift4", rcvr_offset[3]);
+
+	settings->setValue("Modem/ModemType1", speed[0]);
+	settings->setValue("Modem/ModemType2", speed[1]);
+	settings->setValue("Modem/ModemType3", speed[2]);
+	settings->setValue("Modem/ModemType4", speed[3]);
+
+	settings->setValue("Modem/soundChannel1", soundChannel[0]);
+	settings->setValue("Modem/soundChannel2", soundChannel[1]);
+	settings->setValue("Modem/soundChannel3", soundChannel[2]);
+	settings->setValue("Modem/soundChannel4", soundChannel[3]);
+
+	settings->setValue("Modem/DCDThreshold", dcd_threshold);
+	settings->setValue("Modem/rxOffset", rxOffset);
+
+	settings->setValue("AGWHost/Server", AGWServ);
+	settings->setValue("AGWHost/Port", AGWPort);
+	settings->setValue("KISS/Server", KISSServ);
+	settings->setValue("KISS/Port", KISSPort);
+
+	settings->setValue("Modem/PreEmphasisAll1", emph_all[0]);
+	settings->setValue("Modem/PreEmphasisAll2", emph_all[1]);
+	settings->setValue("Modem/PreEmphasisAll3", emph_all[2]);
+	settings->setValue("Modem/PreEmphasisAll4", emph_all[3]);
+
+	settings->setValue("Modem/PreEmphasisDB1", emph_db[0]);
+	settings->setValue("Modem/PreEmphasisDB2", emph_db[1]);
+	settings->setValue("Modem/PreEmphasisDB3", emph_db[2]);
+	settings->setValue("Modem/PreEmphasisDB4", emph_db[3]);
+
+	settings->setValue("Window/Waterfall1", Firstwaterfall);
+	settings->setValue("Window/Waterfall2", Secondwaterfall);
+
+	settings->setValue("Modem/TxDelay1", txdelay[0]);
+	settings->setValue("Modem/TxDelay2", txdelay[1]);
+	settings->setValue("Modem/TxDelay3", txdelay[2]);
+	settings->setValue("Modem/TxDelay4", txdelay[3]);
+
+	settings->setValue("Modem/TxTail1", txtail[0]);
+	settings->setValue("Modem/TxTail2", txtail[1]);
+	settings->setValue("Modem/TxTail3", txtail[2]);
+	settings->setValue("Modem/TxTail4", txtail[3]);
+
+	settings->setValue("Modem/CWIDCall", CWIDCall);
+	settings->setValue("Modem/CWIDMark", CWIDMark);
+	settings->setValue("Modem/CWIDInterval", CWIDInterval);
+	settings->setValue("Modem/CWIDLeft", CWIDLeft);
+	settings->setValue("Modem/CWIDRight", CWIDRight);
+	settings->setValue("Modem/CWIDType", CWIDType);
+	settings->setValue("Modem/afterTraffic", afterTraffic);
+
+	settings->setValue("Init/darkTheme", darkTheme);
+
+	saveAX25Params(0);
+	saveAX25Params(1);
+	saveAX25Params(2);
+	saveAX25Params(3);
+
+	settings->sync();
+
+	delete(settings);
+}
diff --git a/Modulate.c b/Modulate.c
index 86900e3..0eb5f11 100644
--- a/Modulate.c
+++ b/Modulate.c
@@ -1025,6 +1025,34 @@ void sendCWID(char * strID, BOOL CWOnOff, int Chan)
 		initFilter(200, Filter, Chan);
 
 
+	// if sending 1500 cal tone send mark tone for 10 secs
+
+	if (strcmp(strID, "1500TONE") == 0)
+	{
+		float m_amplitude = 30000.0f;
+		float m_frequency = 1500.0f;
+		float m_phase = 0.0;
+		float m_time = 0.0;
+		float m_deltaTime = 1.0f / 12000;
+
+		float x;
+		// generate sin wave in mono
+		for (int sample = 0; sample < 120000; ++sample)
+		{
+			x = m_amplitude * sin(2 * M_PI * m_frequency * m_time + m_phase);
+			ARDOPSampleSink(x);
+			m_time += m_deltaTime;
+		}
+
+
+		ARDOPTXPtr[Chan] = 0;
+		ARDOPTXLen[Chan] = Number;
+		Number = 0;
+
+		return;
+
+	}
+
 	//Generate leader for VOX 6 dots long
 
 	for (k = 6; k >0; k--)
diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp
index 37c6c46..b4ed7d7 100644
--- a/QtSoundModem.cpp
+++ b/QtSoundModem.cpp
@@ -71,6 +71,7 @@ QTextEdit * monWindowCopy;
 extern workerThread *t;
 extern QtSoundModem * w;
 extern QCoreApplication * a;
+extern serialThread *serial;
 
 QList<QSerialPortInfo> Ports = QSerialPortInfo::availablePorts();
 
@@ -95,7 +96,7 @@ extern "C" char CaptureNames[16][256];
 extern "C" char PlaybackNames[16][256];
 
 extern "C" int SoundMode;
-extern "C" int onlyMixSnoop;
+extern "C" bool onlyMixSnoop;
 
 extern "C" int multiCore;
 
@@ -118,6 +119,8 @@ extern "C" int SendSize;		// 100 mS for now
 
 extern "C" int txLatency;
 
+extern "C" int BusyDet;
+
 extern "C"
 { 
 	int InitSound(BOOL Report);
@@ -169,6 +172,8 @@ int Configuring = 0;
 bool lockWaterfall = false;
 bool inWaterfall = false;
 
+int MgmtPort = 0;
+
 extern "C" int NeedWaterfallHeaders;
 extern "C" float BinSize;
 
@@ -192,6 +197,7 @@ QRgb rxText = qRgb(0, 0, 192);
 
 bool darkTheme = true;
 bool minimizeonStart = true;
+extern "C" bool useKISSControls;
 
 // Indexed colour list from ARDOPC
 
@@ -241,6 +247,21 @@ int MintoTray = 1;
 
 int RSID_WF = 0;				// Set to use RSID FFT for Waterfall. 
 
+char SixPackDevice[256] = "";
+int SixPackPort = 0;
+int SixPackEnable = 0;
+
+// Stats
+
+uint64_t PTTonTime[4] = { 0 };
+uint64_t PTTActivemS[4] = { 0 };			// For Stats
+uint64_t BusyonTime[4] = { 0 };
+uint64_t BusyActivemS[4] = { 0 };
+
+int AvPTT[4] = { 0 };
+int AvBusy[4] = { 0 };
+
+
 extern "C" void WriteDebugLog(char * Mess)
 {
 	qDebug() << Mess;
@@ -249,6 +270,24 @@ extern "C" void WriteDebugLog(char * Mess)
 void QtSoundModem::doupdateDCD(int Chan, int State)
 {
 	DCDLabel[Chan]->setVisible(State);
+
+	// This also tries to get a percentage on time over a minute
+
+	uint64_t Time = QDateTime::currentMSecsSinceEpoch();
+
+	if (State)
+	{
+		BusyonTime[Chan] = Time;
+		return;
+	}
+
+	if (BusyonTime[Chan])
+	{
+		BusyActivemS[Chan] += Time - BusyonTime[Chan];
+		BusyonTime[Chan] = 0;
+	}
+
+
 }
 
 extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat);
@@ -529,6 +568,8 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
 
 	getSettings();
 
+	serial = new serialThread;
+
 	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
 
 	family = mysettings.value("FontFamily", "Courier New").toString();
@@ -790,6 +831,10 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
 	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
 	timer->start(100);
 
+	QTimer *statstimer = new QTimer(this);
+	connect(statstimer, SIGNAL(timeout()), this, SLOT(StatsTimer()));
+	statstimer->start(60000);		// One Minute
+
 	wftimer = new QTimer(this);
 	connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF()));
 //	wftimer->start(1000 * 300);
@@ -812,6 +857,9 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
 
 	QTimer::singleShot(200, this, &QtSoundModem::updateFont);
 
+	connect(serial, &serialThread::request, this, &QtSoundModem::showRequest);
+
+
 }
 
 void QtSoundModem::updateFont()
@@ -875,7 +923,113 @@ void extSetOffset(int chan)
 
 	return;
 }
+
+extern TMgmtMode ** MgmtConnections;
+extern int MgmtConCount;
+extern QList<QTcpSocket*>  _MgmtSockets;
+extern "C" void doAGW2MinTimer();
+
+#define FEND 0xc0
+#define QTSMKISSCMD 7
+
+int AGW2MinTimer = 0;
+
+void QtSoundModem::StatsTimer()
+{
+	// Calculate % Busy over last minute
+
+	for (int n = 0; n < 4; n++)
+	{
+		if (soundChannel[n] == 0)	// Channel not used
+			continue;
+		
+		AvPTT[n] = PTTActivemS[n] / 600;		// ms but  want %
+
+		PTTActivemS[n] = 0;
+
+		AvBusy[n] = BusyActivemS[n] / 600;
+		BusyActivemS[n] = 0;
 	
+	
+	// send to any connected Mgmt streams
+
+		char Msg[64];
+		uint64_t ret;
+
+		if (!useKISSControls)
+		{
+
+			for (QTcpSocket* socket : _MgmtSockets)
+			{
+				// Find Session
+
+				TMgmtMode * MGMT = NULL;
+
+				for (int i = 0; i < MgmtConCount; i++)
+				{
+					if (MgmtConnections[i]->Socket == socket)
+					{
+						MGMT = MgmtConnections[i];
+						break;
+					}
+				}
+
+				if (MGMT == NULL)
+					continue;
+
+				if (MGMT->BPQPort[n])
+				{
+					sprintf(Msg, "STATS %d %d %d\r", MGMT->BPQPort[n], AvPTT[n], AvBusy[n]);
+					ret = socket->write(Msg);
+				}
+			}
+		}
+		else			// useKISSControls set
+		{
+			UCHAR * Control = (UCHAR *)malloc(32);
+
+			int len = sprintf((char *)Control, "%c%cSTATS %d %d%c", FEND, (n) << 4 | QTSMKISSCMD, AvPTT[n], AvBusy[n], FEND);
+			KISSSendtoServer(NULL, Control, len);
+		}
+	}
+
+	AGW2MinTimer++;
+
+	if (AGW2MinTimer > 1)
+	{
+		AGW2MinTimer = 0;
+		doAGW2MinTimer();
+	}
+}
+
+// PTT Stats
+
+extern "C" void UpdatePTTStats(int Chan, int State)
+{
+	uint64_t Time = QDateTime::currentMSecsSinceEpoch();
+
+	if (State)
+	{
+		PTTonTime[Chan] = Time;
+
+		// Cancel Busy timer (stats include ptt on time in port active
+
+		if (BusyonTime[Chan])
+		{
+			BusyActivemS[Chan] += (Time - BusyonTime[Chan]);
+			BusyonTime[Chan] = 0;
+		}
+	}
+	else
+	{
+		if (PTTonTime[Chan])
+		{
+			PTTActivemS[Chan] += (Time - PTTonTime[Chan]);
+			PTTonTime[Chan] = 0;
+		}
+	}
+}
+
 void QtSoundModem::MyTimerSlot()
 {
 	// 100 mS Timer Event
@@ -1083,6 +1237,8 @@ void QtSoundModem::clickedSlotI(int i)
 	if (strcmp(Name, "DCDSlider") == 0)
 	{
 		dcd_threshold = i;
+		BusyDet = i / 10;		// for ardop busy detect code
+
 		saveSettings();
 		return;
 	}
@@ -1264,6 +1420,16 @@ void QtSoundModem::clickedSlot()
 		return;
 	}
 
+	if (strcmp(Name, "Cal1500") == 0)
+	{
+		char call[] = "1500TONE";
+		sendCWID(call, 0, 0);
+		calib_mode[0] = 4;
+		return;
+	}
+
+
+
 	if (strcmp(Name, "actFont") == 0)
 	{
 		bool ok;
@@ -2009,6 +2175,7 @@ bool myResize::eventFilter(QObject *obj, QEvent *event)
 void QtSoundModem::doDevices()
 {
 	char valChar[10];
+	QStringList items;
 
 	Dev = new(Ui_devicesDialog);
 
@@ -2025,6 +2192,22 @@ void QtSoundModem::doDevices()
 
 	UI.installEventFilter(resize);
 
+	// Set serial names
+
+	for (const QSerialPortInfo &info : Ports)
+	{
+		items.append(info.portName());
+	}
+
+	items.sort();
+
+	Dev->SixPackSerial->addItem("None");
+
+	for (const QString &info : items)
+	{
+		Dev->SixPackSerial->addItem(info);
+	}
+
 	newSoundMode = SoundMode;
 	oldSoundMode = SoundMode;
 	oldSnoopMix = newSnoopMix = onlyMixSnoop;
@@ -2100,6 +2283,7 @@ void QtSoundModem::doDevices()
 	QStandardItem * item = model->item(0, 0);
 	item->setEnabled(false);
 
+	Dev->useKISSControls->setChecked(useKISSControls);
 	Dev->singleChannelOutput->setChecked(SCO);
 	Dev->colourWaterfall->setChecked(raduga);
 
@@ -2111,6 +2295,26 @@ void QtSoundModem::doDevices()
 	Dev->AGWPort->setText(valChar);
 	Dev->AGWEnabled->setChecked(AGWServ);
 
+	Dev->MgmtPort->setText(QString::number(MgmtPort));
+
+	// If we are using a user specifed device add it
+
+	i = Dev->SixPackSerial->findText(SixPackDevice, Qt::MatchFixedString);
+
+	if (i == -1)
+	{
+		// Add our device to list
+
+		Dev->SixPackSerial->insertItem(0, SixPackDevice);
+		i = Dev->SixPackSerial->findText(SixPackDevice, Qt::MatchContains);
+	}
+
+	Dev->SixPackSerial->setCurrentIndex(i);
+
+	sprintf(valChar, "%d", SixPackPort);
+	Dev->SixPackTCP->setText(valChar);
+	Dev->SixPackEnable->setChecked(SixPackEnable);
+
 	Dev->PTTOn->setText(PTTOnString);
 	Dev->PTTOff->setText(PTTOffString);
 
@@ -2137,8 +2341,6 @@ void QtSoundModem::doDevices()
 
 	Dev->VIDPID->setText(CM108Addr);
 
-	QStringList items;
-
 	connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool)));
 	connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool)));
 	connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));
@@ -2148,13 +2350,6 @@ void QtSoundModem::doDevices()
 	else
 		Dev->RTSDTR->setChecked(true);
 
-	for (const QSerialPortInfo &info : Ports)
-	{
-		items.append(info.portName());
-	}
-
-	items.sort();
-
 	Dev->PTTPort->addItem("None");
 	Dev->PTTPort->addItem("CM108");
 
@@ -2342,6 +2537,8 @@ void QtSoundModem::deviceaccept()
 	if (UsingLeft && UsingRight)
 		UsingBothChannels = 1;
 
+
+	useKISSControls = Dev->useKISSControls->isChecked();
 	SCO = Dev->singleChannelOutput->isChecked();
 	raduga = Dev->colourWaterfall->isChecked();
 	AGWServ = Dev->AGWEnabled->isChecked();
@@ -2353,9 +2550,26 @@ void QtSoundModem::deviceaccept()
 	Q = Dev->AGWPort->text();
 	AGWPort = Q.toInt();
 
+	Q = Dev->MgmtPort->text();
+	MgmtPort = Q.toInt();
+
+	Q = Dev->SixPackSerial->currentText();
+
+	char temp[256];
+
+	strcpy(temp, Q.toString().toUtf8());
+
+	if (strlen(temp))
+		strcpy(SixPackDevice, temp);
+
+	Q = Dev->SixPackTCP->text();
+	SixPackPort = Q.toInt();
+
+	SixPackEnable = Dev->SixPackEnable->isChecked();
+
+
 	Q = Dev->PTTPort->currentText();
 	
-	char temp[256];
 
 	strcpy(temp, Q.toString().toUtf8());
 
@@ -2588,6 +2802,7 @@ void QtSoundModem::doCalibrate()
 		connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot()));
 		connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot()));
 		connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Cal1500, SIGNAL(released()), this, SLOT(clickedSlot()));
 
 		/*
 		
@@ -3502,7 +3717,7 @@ extern "C" int openTraceLog()
 	return 1;
 }
 
-extern "C" qint64 writeTraceLog(char * Data, char Dirn)
+extern "C" qint64 writeTraceLog(char * Data)
 {
 	return tracefile.write(Data);
 }
@@ -3534,7 +3749,7 @@ extern "C" void debugTimeStamp(char * Text, char Dirn)
 	char Msg[2048];
 
 	sprintf(Msg, "%s %s\n", String.toUtf8().data(), Text);
-	qint64 ret = writeTraceLog(Msg, Dirn);
+	writeTraceLog(Msg);
 }
 
 
@@ -3599,3 +3814,76 @@ void QtSoundModem::StartWatchdog()
  }
 
 
+ // KISS Serial Port code - mainly for 6Pack but should work with KISS as well
+
+ // Serial Read needs to block and signal the main thread whenever a character is received. TX can probably be uncontrolled
+
+ void serialThread::startSlave(const QString &portName, int waitTimeout, const QString &response)
+ {
+	 QMutexLocker locker(&mutex);
+	 this->portName = portName;
+	 this->waitTimeout = waitTimeout;
+	 this->response = response;
+	 if (!isRunning())
+		 start();
+ }
+
+ void serialThread::run()
+ {
+	 QSerialPort serial;
+	 bool currentPortNameChanged = false;
+
+	 mutex.lock();
+	 QString currentPortName;
+	 if (currentPortName != portName) {
+		 currentPortName = portName;
+		 currentPortNameChanged = true;
+	 }
+
+	 int currentWaitTimeout = waitTimeout;
+	 QString currentRespone = response;
+	 mutex.unlock();
+
+	 if (currentPortName.isEmpty())
+	 {
+		 Debugprintf("Port not set");
+		 return;
+	 }
+
+	 serial.setPortName(currentPortName);
+
+	 if (!serial.open(QIODevice::ReadWrite))
+	 {
+		 Debugprintf("Can't open %s, error code %d", portName, serial.error());
+		 return;
+	 }
+ 
+	 while (1)
+	 {
+		
+		 if (serial.waitForReadyRead(currentWaitTimeout))
+		 {
+			 // read request
+			 QByteArray requestData = serial.readAll();
+			 while (serial.waitForReadyRead(10))
+				 requestData += serial.readAll();
+
+			 // Pass data to 6pack handler
+
+			 emit this->request(requestData);
+
+		 }
+		 else {
+			 Debugprintf("Serial read request timeout");
+		 }
+	 }
+ }
+
+ void Process6PackData(unsigned char * Bytes, int Len);
+
+ void QtSoundModem::showRequest(QByteArray Data)
+ {
+	 Process6PackData((unsigned char *)Data.data(), Data.length());
+ }
+
+ 
\ No newline at end of file
diff --git a/QtSoundModem.cpp.bak b/QtSoundModem.cpp.bak
index 1dd6aa1..fda07f6 100644
--- a/QtSoundModem.cpp.bak
+++ b/QtSoundModem.cpp.bak
@@ -1,3380 +1,3380 @@
-/*
-Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
-
-This file is part of QtSoundModem
-
-QtSoundModem is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-QtSoundModem is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with QtSoundModem.  If not, see http://www.gnu.org/licenses
-
-*/
-
-// UZ7HO Soundmodem Port by John Wiseman G8BPQ
-
-// UZ7HO Soundmodem Port
-
-// Not Working 4psk100 FEC 
-
-// Thoughts on Waterfall Display.
-
-// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum
-
-// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size?
-
-// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273
-
-#include "QtSoundModem.h"
-#include <qheaderview.h>
-//#include <QDebug>
-#include <QHostAddress>
-#include <QAbstractSocket>
-#include <QSettings>
-#include <QPainter>
-#include <QtSerialPort/QSerialPort>
-#include <QtSerialPort/QSerialPortInfo>
-#include <QMessageBox>
-#include <QTimer>
-#include <qevent.h>
-#include <QStandardItemModel>
-#include <QScrollBar>
-#include <QFontDialog>
-#include "UZ7HOStuff.h"
-
-
-QImage *Constellation[4];
-QImage *Waterfall = 0;
-QLabel *DCDLabel[4];
-QLineEdit *chanOffsetLabel[4];
-QImage *DCDLed[4];
-
-QImage *RXLevel;
-QImage *RXLevel2;
-
-QLabel *WaterfallCopy;
-
-QLabel * RXLevelCopy;
-QLabel * RXLevel2Copy;
-
-QTextEdit * monWindowCopy;
-
-extern workerThread *t;
-extern QtSoundModem * w;
-extern QCoreApplication * a;
-
-QList<QSerialPortInfo> Ports = QSerialPortInfo::availablePorts();
-
-void saveSettings();
-void getSettings();
-void DrawModemFreqRange();
-
-extern "C" void CloseSound();
-extern "C" void GetSoundDevices();
-extern "C" char modes_name[modes_count][21];
-extern "C" int speed[5];
-extern "C" int KISSPort;
-extern "C" short rx_freq[5];
-
-extern "C" int CaptureCount;
-extern "C" int PlaybackCount;
-
-extern "C" int CaptureIndex;		// Card number
-extern "C" int PlayBackIndex;
-
-extern "C" char CaptureNames[16][256];
-extern "C" char PlaybackNames[16][256];
-
-extern "C" int SoundMode;
-extern "C" int multiCore;
-
-extern "C" int refreshModems;
-int NeedPSKRefresh;
-
-extern "C" int pnt_change[5];
-extern "C" int needRSID[4];
-
-extern "C" int needSetOffset[4];
-
-extern "C" float MagOut[4096];
-extern "C" float MaxMagOut;
-extern "C" int MaxMagIndex;
-
-
-extern "C" int using48000;			// Set if using 48K sample rate (ie RUH Modem active)
-extern "C" int ReceiveSize;
-extern "C" int SendSize;		// 100 mS for now
-
-extern "C"
-{ 
-	int InitSound(BOOL Report);
-	void soundMain();
-	void MainLoop();
-	void modulator(UCHAR snd_ch, int buf_size);
-	void SampleSink(int LR, short Sample);
-	void doCalib(int Port, int Act);
-	int Freq_Change(int Chan, int Freq);
-	void set_speed(int snd_ch, int Modem);
-	void init_speed(int snd_ch);
-	void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform);
-	void dofft(short * in, float * outr, float * outi);
-	void init_raduga();
-	void DrawFreqTicks();
-	void AGW_Report_Modem_Change(int port);
-	char * strlop(char * buf, char delim);
-	void sendRSID(int Chan, int dropTX);
-	void RSIDinitfft();
-	void il2p_init(int il2p_debug);
-}
-
-void make_graph_buf(float * buf, short tap, QPainter * bitmap);
-
-int ModemA = 2;
-int ModemB = 2;
-int ModemC = 2;
-int ModemD = 2;
-int FreqA = 1500;
-int FreqB = 1500;
-int FreqC = 1500;
-int FreqD = 1500;
-int DCD = 50;
-
-char CWIDCall[128] = "";
-extern "C" char CWIDMark[32] = "";
-int CWIDInterval = 0;
-int CWIDLeft = 0;
-int CWIDRight = 0;
-int CWIDType = 1;			// on/off
-bool afterTraffic = 0;
-bool cwidtimerisActive = false;
-
-int WaterfallMin = 00;
-int WaterfallMax = 6000;
-
-int Configuring = 0;
-bool lockWaterfall = false;
-bool inWaterfall = false;
-
-extern "C" int NeedWaterfallHeaders;
-extern "C" float BinSize;
-
-extern "C" { int RSID_SABM[4]; }
-extern "C" { int RSID_UI[4]; }
-extern "C" { int RSID_SetModem[4]; }
-extern "C" unsigned int pskStates[4];
-
-int Closing = FALSE;				// Set to stop background thread
-
-QRgb white = qRgb(255, 255, 255);
-QRgb black = qRgb(0, 0, 0);
-
-QRgb green = qRgb(0, 255, 0);
-QRgb red = qRgb(255, 0, 0);
-QRgb yellow = qRgb(255, 255, 0);
-QRgb cyan = qRgb(0, 255, 255);
-
-QRgb txText = qRgb(192, 0, 0);
-QRgb rxText = qRgb(0, 0, 192);
-
-bool darkTheme = true;
-bool minimizeonStart = true;
-
-// Indexed colour list from ARDOPC
-
-#define WHITE 0
-#define Tomato 1
-#define Gold 2
-#define Lime 3
-#define Yellow 4
-#define Orange 5
-#define Khaki 6
-#define Cyan 7
-#define DeepSkyBlue 8
-#define RoyalBlue 9
-#define Navy 10
-#define Black 11
-#define Goldenrod 12
-#define Fuchsia 13
-
-QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0),
-						qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255),
-						qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0),
-						qRgb(218, 165, 32), qRgb(255, 0, 255) };
-
-unsigned char  WaterfallLines[2][80][4096] = { 0 };
-int NextWaterfallLine[2] = {0, 0};
-
-unsigned int LastLevel = 255;
-unsigned int LastBusy = 255;
-
-extern "C" int UDPClientPort;
-extern "C" int UDPServerPort;
-extern "C" int TXPort;
-extern char UDPHost[64];
-
-QTimer *cwidtimer;
-QWidget * mythis;
-
-
-QSystemTrayIcon * trayIcon = nullptr;
-
-int MintoTray = 1;
-
-int RSID_WF = 0;				// Set to use RSID FFT for Waterfall. 
-
-extern "C" void WriteDebugLog(char * Mess)
-{
-	qDebug() << Mess;
-}
-
-void QtSoundModem::doupdateDCD(int Chan, int State)
-{
-	DCDLabel[Chan]->setVisible(State);
-}
-
-extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat);
-extern "C" char * ShortDateTime();
-
-extern "C" void mon_rsid(int snd_ch, char * RSID)
-{
-	int Len;
-	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot
-
-	sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R');
-
-	Len = strlen(Msg);
-
-	if (Msg[Len - 1] != '\r')
-	{
-		Msg[Len++] = '\r';
-		Msg[Len] = 0;
-	}
-
-	emit t->sendtoTrace(Msg, 0);
-}
-
-extern "C" void put_frame(int snd_ch, string * frame, char * code, int  tx, int excluded)
-{
-	UNUSED(excluded);
-
-	int Len;
-	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot
-
-	if (strcmp(code, "NON-AX25") == 0)
-		sprintf(Msg, "%d: <NON-AX25 frame Len = %d [%s%c]\r", snd_ch, frame->Length, ShortDateTime(), 'R');
-	else
-		sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx));
-
-	Len = strlen(Msg);
-
-	if (Msg[Len - 1] != '\r')
-	{
-		Msg[Len++] = '\r';
-		Msg[Len] = 0;
-	}
-
-	emit t->sendtoTrace(Msg, tx);
-}
-
-extern "C" void updateDCD(int Chan, bool State)
-{
-	emit t->updateDCD(Chan, State);
-}
-
-bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt)
-{
-	UNUSED(obj);
-
-	if (evt->type() == QEvent::Resize)
-	{
-		return QWidget::event(evt);
-	}
-
-	if (evt->type() == QEvent::WindowStateChange)
-	{
-		if (windowState().testFlag(Qt::WindowMinimized) == true)
-			w_state = WIN_MINIMIZED;
-		else
-			w_state = WIN_MAXIMIZED;
-	}
-//	if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma;
-//	{
-//		qDebug() << "App State changed =" << evt->type() << endl;
-//	}
-
-	return QWidget::event(evt);
-}
-
-void QtSoundModem::resizeEvent(QResizeEvent* event)
-{
-	QMainWindow::resizeEvent(event);
-
-	QRect r = geometry();
-
-	QRect r1 = ui.monWindow->geometry();
-	QRect r2 = ui.centralWidget->geometry();
-
-	int modemBoxHeight = 34;
-	
-	int Width = r.width();
-	int Height = r.height() - 25;
-
-	int monitorTop;
-	int monitorHeight;
-	int sessionTop;
-	int sessionHeight = 0;
-	int waterfallsTop;
-	int waterfallsHeight = WaterfallImageHeight;
-
-	ui.modeB->setVisible(soundChannel[1]);
-	ui.centerB->setVisible(soundChannel[1]);
-	ui.labelB->setVisible(soundChannel[1]);
-	DCDLabel[1]->setVisible(soundChannel[1]);
-	ui.RXOffsetB->setVisible(soundChannel[1]);
-
-	ui.modeC->setVisible(soundChannel[2]);
-	ui.centerC->setVisible(soundChannel[2]);
-	ui.labelC->setVisible(soundChannel[2]);
-	DCDLabel[2]->setVisible(soundChannel[2]);
-	ui.RXOffsetC->setVisible(soundChannel[2]);
-
-	ui.modeD->setVisible(soundChannel[3]);
-	ui.centerD->setVisible(soundChannel[3]);
-	ui.labelD->setVisible(soundChannel[3]);
-	DCDLabel[3]->setVisible(soundChannel[3]);
-	ui.RXOffsetD->setVisible(soundChannel[3]);
-
-	if (soundChannel[2] || soundChannel[3])
-		modemBoxHeight = 60;
-
-	ui.Waterfall->setVisible(0);
-
-	monitorTop = modemBoxHeight + 1;
-
-	// Now have one waterfall label containing headers and waterfalls
-
-		if (Firstwaterfall || Secondwaterfall)
-			ui.Waterfall->setVisible(1);
-
-	if (AGWServ)
-	{
-		sessionTable->setVisible(true);
-		sessionHeight = 150;
-	}
-	else
-	{
-		sessionTable->setVisible(false);
-	}
-
-	// if only displaying one Waterfall, change height of waterfall area
-
-	if (UsingBothChannels == 0  || (Firstwaterfall == 0) || (Secondwaterfall == 0))
-	{
-		waterfallsHeight /= 2;
-	}
-
-	if ((Firstwaterfall == 0) && (Secondwaterfall == 0))
-		waterfallsHeight = 0;
-
-	monitorHeight = Height - sessionHeight - waterfallsHeight - modemBoxHeight;
-	waterfallsTop = Height - waterfallsHeight;
-	sessionTop = Height - (sessionHeight + waterfallsHeight);
-
-	ui.monWindow->setGeometry(QRect(0, monitorTop, Width, monitorHeight));
-
-	if (AGWServ)
-		sessionTable->setGeometry(QRect(0, sessionTop, Width, sessionHeight));
-
-	if (waterfallsHeight)
-		ui.Waterfall->setGeometry(QRect(0, waterfallsTop, Width, waterfallsHeight + 2));
-}
-
-QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State)
-{
-	QAction * Act = new QAction(Label, parent);
-	Menu->addAction(Act);
-
-	Act->setCheckable(true);
-	if (State)
-		Act->setChecked(true);
-
-	parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked()));
-
-	return Act;
-}
-
-void QtSoundModem::menuChecked()
-{
-	QAction * Act = static_cast<QAction*>(QObject::sender());
-
-	int state = Act->isChecked();
-
-	if (Act == actWaterfall1)
-		Firstwaterfall = state;
-
-	else if (Act == actWaterfall2)
-		Secondwaterfall = state;
-
-	initWaterfall(Firstwaterfall | Secondwaterfall);
-
-	saveSettings();
-}
-
-void QtSoundModem::initWaterfall(int state)
-{
-	if (state == 1)
-	{
-	//	if (ui.Waterfall)
-	//	{
-	//		delete ui.Waterfall;
-	//		ui.Waterfall = new QLabel(ui.centralWidget);
-	//	}
-		WaterfallCopy = ui.Waterfall;
-
-		Waterfall = new QImage(1024, WaterfallImageHeight + 2, QImage::Format_RGB32);
-
-		NeedWaterfallHeaders = 1;
-
-	}
-	else
-	{
-		delete(Waterfall);
-		Waterfall = 0;
-	}
-
-	QSize Size(800, 602);						// Not actually used, but Event constructor needs it
-
-	QResizeEvent *event = new QResizeEvent(Size, Size);
-	QApplication::sendEvent(this, event);
-}
-
-QRect PSKRect = { 100,100,100,100 };
-
-QDialog * constellationDialog;
-QLabel * constellationLabel[4];
-QLabel * QualLabel[4];
-
-// Local copies
-
-QLabel *RXOffsetLabel;
-QSlider *RXOffset;
-
-QFont Font;
-
-extern "C" void CheckPSKWindows()
-{
-	NeedPSKRefresh = 1;
-}
-void DoPSKWindows()
-{
-	// Display Constellation for PSK Window;
-
-	int NextX = 0;
-	int i;
-
-	for (i = 0; i < 4; i++)
-	{
-		if (soundChannel[i] && pskStates[i])
-		{
-			constellationLabel[i]->setGeometry(QRect(NextX, 19, 121, 121));
-			QualLabel[i]->setGeometry(QRect(1 + NextX, 1, 120, 15));
-			constellationLabel[i]->setVisible(1);
-			QualLabel[i]->setVisible(1);
-
-			NextX += 122;
-		}
-		else
-		{
-			constellationLabel[i]->setVisible(0);
-			QualLabel[i]->setVisible(0);
-		}
-	}
-	constellationDialog->resize(NextX, 140);
-}
-
-QTimer *wftimer;
-
-QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
-{
-	QString family;
-	int csize;
-	QFont::Weight weight;
-
-	ui.setupUi(this);
-
-	mythis = this;
-
-	getSettings();
-
-	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
-
-	family = mysettings.value("FontFamily", "Courier New").toString();
-	csize = mysettings.value("PointSize", 0).toInt();
-	weight = (QFont::Weight)mysettings.value("Weight", 50).toInt();
-
-	Font = QFont(family);
-	Font.setPointSize(csize);
-	Font.setWeight(weight);
-
-	QApplication::setFont(Font);
-
-	constellationDialog = new QDialog(nullptr, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
-	constellationDialog->resize(488, 140);
-	constellationDialog->setGeometry(PSKRect);
-
-	QFont f("Arial", 8, QFont::Normal);
-
-	for (int i = 0; i < 4; i++)
-	{
-		char Text[16];
-		sprintf(Text, "Chan %c", i + 'A');
-
-		constellationLabel[i] = new QLabel(constellationDialog);
-		constellationDialog->setWindowTitle("PSK Constellations");
-
-		QualLabel[i] = new QLabel(constellationDialog);
-		QualLabel[i]->setText(Text);
-		QualLabel[i]->setFont(f);
-
-		Constellation[i] = new QImage(121, 121, QImage::Format_RGB32);
-		Constellation[i]->fill(black);
-		constellationLabel[i]->setPixmap(QPixmap::fromImage(*Constellation[i]));
-	}
-	constellationDialog->show();
-
-	if (MintoTray)
-	{
-		char popUp[256];
-		sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort);
-		trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this);
-		trayIcon->setToolTip(popUp);
-		trayIcon->show();
-		
-		connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason)));
-	}
-
-	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
-	ReceiveSize = 512;
-	SendSize = 1024;		// 100 mS for now
-
-	for (int i = 0; i < 4; i++)
-	{
-		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
-		{
-			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
-			ReceiveSize = 2048;
-			SendSize = 4096;		// 100 mS for now
-		}
-	}
-
-	float FFTCalc = 12000.0f / ((WaterfallMax - WaterfallMin) / 900.0f);
-
-	FFTSize = FFTCalc + 0.4999;
-
-	if (FFTSize > 8191)
-		FFTSize = 8190;
-
-	if (FFTSize & 1)		// odd
-		FFTSize--;
-
-	BinSize = 12000.0 / FFTSize;
-
-	restoreGeometry(mysettings.value("geometry").toByteArray());
-	restoreState(mysettings.value("windowState").toByteArray());
-
-	sessionTable = new QTableWidget(ui.centralWidget);
-
-	sessionTable->verticalHeader()->setVisible(FALSE);
-	sessionTable->verticalHeader()->setDefaultSectionSize(20);
-	sessionTable->horizontalHeader()->setDefaultSectionSize(68);
-	sessionTable->setRowCount(1);
-	sessionTable->setColumnCount(12);
-	m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction";
-
-	mysetstyle();
-
-	sessionTable->setHorizontalHeaderLabels(m_TableHeader);
-	sessionTable->setColumnWidth(0, 80);
-	sessionTable->setColumnWidth(1, 80);
-	sessionTable->setColumnWidth(4, 76);
-	sessionTable->setColumnWidth(5, 76);
-	sessionTable->setColumnWidth(6, 80);
-	sessionTable->setColumnWidth(11, 72);
-
-	for (int i = 0; i < modes_count; i++)
-	{
-		ui.modeA->addItem(modes_name[i]);
-		ui.modeB->addItem(modes_name[i]);
-		ui.modeC->addItem(modes_name[i]);
-		ui.modeD->addItem(modes_name[i]);
-	}
-
-	// Set up Menus
-
-	setupMenu = ui.menuBar->addMenu(tr("Settings"));
-
-	actDevices = new QAction("Setup Devices", this);
-	setupMenu->addAction(actDevices);
-
-	connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot()));
-	actDevices->setObjectName("actDevices");
-	actModems = new QAction("Setup Modems", this);
-	actModems->setObjectName("actModems");
-	setupMenu->addAction(actModems);
-
-	connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot()));
-
-	actFont = new QAction("Setup Font", this);
-	actFont->setObjectName("actFont");
-	setupMenu->addAction(actFont);
-
-	connect(actFont, SIGNAL(triggered()), this, SLOT(clickedSlot()));
-
-	actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray()));
-	actMintoTray->setCheckable(1);
-	actMintoTray->setChecked(MintoTray);
-
-	viewMenu = ui.menuBar->addMenu(tr("&View"));
-
-	actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall);
-	actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall);
-
-	actCalib = ui.menuBar->addAction("&Calibration");
-	connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate()));
-
-	actRestartWF = ui.menuBar->addAction("Restart Waterfall");
-	connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF()));
-
-	actAbout = ui.menuBar->addAction("&About");
-	connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout()));
-
-	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
-	RXLevel->fill(white);
-	ui.RXLevel->setPixmap(QPixmap::fromImage(*RXLevel));
-	RXLevelCopy = ui.RXLevel;
-
-	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
-	RXLevel2->fill(white);
-	ui.RXLevel2->setPixmap(QPixmap::fromImage(*RXLevel2));
-	RXLevel2Copy = ui.RXLevel2;
-
-	DCDLabel[0] = new QLabel(this);
-	DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA"));
-	DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12));
-	DCDLabel[0]->setVisible(TRUE);
-
-	DCDLabel[1] = new QLabel(this);
-	DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB"));
-	DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12));
-	DCDLabel[1]->setVisible(TRUE);
-
-	DCDLabel[2] = new QLabel(this);
-	DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC"));
-	DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12));
-	DCDLabel[2]->setVisible(FALSE);
-
-	DCDLabel[3] = new QLabel(this);
-	DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD"));
-	DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12));
-	DCDLabel[3]->setVisible(FALSE);
-	
-	DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32);
-	DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32);
-	DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32);
-	DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32);
-
-	DCDLed[0]->fill(red);
-	DCDLed[1]->fill(red);
-	DCDLed[2]->fill(red);
-	DCDLed[3]->fill(red);
-
-	DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0]));
-	DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1]));
-	DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2]));
-	DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3]));
-
-	chanOffsetLabel[0] = ui.RXOffsetA;
-	chanOffsetLabel[1] = ui.RXOffsetB;
-	chanOffsetLabel[2] = ui.RXOffsetC;
-	chanOffsetLabel[3] = ui.RXOffsetD;
-
-	WaterfallCopy = ui.Waterfall;
-
-	initWaterfall(Firstwaterfall | Secondwaterfall);
-
-	monWindowCopy = ui.monWindow;
-
-	ui.monWindow->document()->setMaximumBlockCount(10000);
-
-//	connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged()));
-
-	connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
-
-	ModemA = speed[0];
-	ModemB = speed[1];
-	ModemC = speed[2];
-	ModemD = speed[3];
-
-	ui.modeA->setCurrentIndex(speed[0]);
-	ui.modeB->setCurrentIndex(speed[1]);
-	ui.modeC->setCurrentIndex(speed[2]);
-	ui.modeD->setCurrentIndex(speed[3]);
-
-	ModemA = ui.modeA->currentIndex();
-
-	ui.centerA->setValue(rx_freq[0]);
-	ui.centerB->setValue(rx_freq[1]);
-	ui.centerC->setValue(rx_freq[2]);
-	ui.centerD->setValue(rx_freq[3]);
-
-	connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
-
-	ui.DCDSlider->setValue(dcd_threshold);
-
-	char valChar[32];
-	sprintf(valChar, "RX Offset %d", rxOffset);
-	ui.RXOffsetLabel->setText(valChar);
-	ui.RXOffset->setValue(rxOffset);
-
-	RXOffsetLabel = ui.RXOffsetLabel;
-	RXOffset = ui.RXOffset;
-
-	connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int)));
-	connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
-
-	QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection);
-	QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection);
-
-	QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), Qt::QueuedConnection);
-
-	connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
-	connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
-	connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
-	connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
-
-	QTimer *timer = new QTimer(this);
-	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
-	timer->start(100);
-
-	wftimer = new QTimer(this);
-	connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF()));
-	wftimer->start(1000 * 300);
-
-	cwidtimer = new QTimer(this);
-	connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer()));
-
-
-	if (CWIDInterval && afterTraffic == false)
-		cwidtimer->start(CWIDInterval * 60000);
-
-	if (RSID_SetModem[0])
-	{
-		RSID_WF = 1;
-		RSIDinitfft();
-	}
-//	il2p_init(1);
-
-	QTimer::singleShot(200, this, &QtSoundModem::updateFont);
-
-}
-
-void QtSoundModem::updateFont()
-{
-	QApplication::setFont(Font);
-}
-
-void QtSoundModem::MinimizetoTray()
-{
-	MintoTray = actMintoTray->isChecked();
-	saveSettings();
-	QMessageBox::about(this, tr("QtSoundModem"),
-	tr("Program must be restarted to change Minimize mode"));
-}
-
-
-void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason)
-{
-	if (reason == 3)
-	{
-		showNormal();
-		w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
-	} 
-}
-
-extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan);
-
-extern "C" void checkforCWID()
-{
-	emit(t->startCWIDTimer());
-};
-
-extern "C" void QtSoundModem::startCWIDTimerSlot()
-{
-	if (CWIDInterval && afterTraffic == 1 && cwidtimerisActive == false)
-	{
-		cwidtimerisActive = true;
-		QTimer::singleShot(CWIDInterval * 60000, this, &QtSoundModem::CWIDTimer);
-	}
-}
-
-void QtSoundModem::CWIDTimer()
-{
-	cwidtimerisActive = false;
-	sendCWID(CWIDCall, CWIDType, 0);
-	calib_mode[0] = 4;
-}
-
-void extSetOffset(int chan)
-{
-	char valChar[32];
-	sprintf(valChar, "%d", chanOffset[chan]);
-	chanOffsetLabel[chan]->setText(valChar);
-
-	NeedWaterfallHeaders = true;
-
-	pnt_change[0] = 1;
-	pnt_change[1] = 1;
-	pnt_change[2] = 1;
-	pnt_change[3] = 1;
-
-	return;
-}
-	
-void QtSoundModem::MyTimerSlot()
-{
-	// 100 mS Timer Event
-
-	for (int i = 0; i < 4; i++)
-	{
-
-		if (needSetOffset[i])
-		{
-			needSetOffset[i] = 0;
-			extSetOffset(i);						// Update GUI
-		}
-	}
-
-	if (refreshModems)
-	{
-		refreshModems = 0;
-
-		ui.modeA->setCurrentIndex(speed[0]);
-		ui.modeB->setCurrentIndex(speed[1]);
-		ui.modeC->setCurrentIndex(speed[2]);
-		ui.modeD->setCurrentIndex(speed[3]);
-		ui.centerA->setValue(rx_freq[0]);
-		ui.centerB->setValue(rx_freq[1]);
-		ui.centerC->setValue(rx_freq[2]);
-		ui.centerD->setValue(rx_freq[3]);
-	}
-	if (NeedPSKRefresh)
-	{
-		NeedPSKRefresh = 0;
-		DoPSKWindows();
-	}
-
-	if (NeedWaterfallHeaders)
-	{
-		NeedWaterfallHeaders = 0;
-
-		if (Waterfall)
-		{
-			Waterfall->fill(black);
-			DrawModemFreqRange();
-			DrawFreqTicks();
-		}
-	}
-
-	show_grid();
-}
-
-void QtSoundModem::returnPressed()
-{
-	char Name[32];
-	int Chan;
-	QString val;
-	
-	strcpy(Name, sender()->objectName().toUtf8());
-
-	Chan = Name[8] - 'A';
-
-	val = chanOffsetLabel[Chan]->text();
-
-	chanOffset[Chan] = val.toInt();
-	needSetOffset[Chan] = 1;				// Update GUI
-
-
-}
-
-void CheckforChanges(int Mode, int OldMode)
-{
-	int old48000 = using48000;
-
-	if (OldMode != Mode && Mode == 15)
-	{
-		QMessageBox msgBox;
-
-		msgBox.setText("Warning!!\nARDOP Packet is NOT the same as ARDOP\n"
-			"It is an experimental mode for sending ax.25 frames using ARDOP packet formats\n");
-
-		msgBox.setStandardButtons(QMessageBox::Ok);
-
-		msgBox.exec();
-	}
-
-	// See if need to switch beween 12000 and 48000
-
-	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
-	ReceiveSize = 512;
-	SendSize = 1024;		// 100 mS for now
-
-	for (int i = 0; i < 4; i++)
-	{
-		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
-		{
-			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
-			ReceiveSize = 2048;
-			SendSize = 4096;		// 100 mS for now
-		}
-	}
-
-	if (using48000 != old48000)
-	{
-		InitSound(1);
-	}
-}
-
-
-void QtSoundModem::clickedSlotI(int i)
-{
-	char Name[32];
-
-	strcpy(Name, sender()->objectName().toUtf8());
-
-	if (strcmp(Name, "modeA") == 0)
-	{
-		int OldModem = ModemA;
-		ModemA = ui.modeA->currentIndex();
-		set_speed(0, ModemA);
-		CheckforChanges(ModemA, OldModem);
-		saveSettings();
-		AGW_Report_Modem_Change(0);
-		return;
-	}
-
-	if (strcmp(Name, "modeB") == 0)
-	{
-		int OldModem = ModemB;
-		ModemB = ui.modeB->currentIndex();
-		set_speed(1, ModemB);
-		CheckforChanges(ModemB, OldModem);
-		saveSettings();
-		AGW_Report_Modem_Change(1);
-		return;
-	}
-
-	if (strcmp(Name, "modeC") == 0)
-	{
-		int OldModem = ModemC;
-		ModemC = ui.modeC->currentIndex();
-		set_speed(2, ModemC);
-		CheckforChanges(ModemC, OldModem);
-		saveSettings();
-		AGW_Report_Modem_Change(2);
-		return;
-	}
-
-	if (strcmp(Name, "modeD") == 0)
-	{
-		int OldModem = ModemD;
-		ModemD = ui.modeD->currentIndex();
-		set_speed(3, ModemD);
-		CheckforChanges(ModemD, OldModem);
-		saveSettings();
-		AGW_Report_Modem_Change(3);
-		return;
-	}
-
-	if (strcmp(Name, "centerA") == 0)
-	{
-		if (i > 299)
-		{
-			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-			ui.centerA->setValue(Freq_Change(0, i));
-			settings->setValue("Modem/RXFreq1", ui.centerA->value());
-			AGW_Report_Modem_Change(0);
-
-		}
-		return;
-	}
-
-	if (strcmp(Name, "centerB") == 0)
-	{
-		if (i > 299)
-		{
-			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-			ui.centerB->setValue(Freq_Change(1, i));
-			settings->setValue("Modem/RXFreq2", ui.centerB->value());
-			AGW_Report_Modem_Change(1);
-		}
-		return;
-	}
-
-	if (strcmp(Name, "centerC") == 0)
-	{
-		if (i > 299)
-		{
-			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-			ui.centerC->setValue(Freq_Change(2, i));
-			settings->setValue("Modem/RXFreq3", ui.centerC->value());
-			AGW_Report_Modem_Change(2);
-		}
-		return;
-	}
-
-	if (strcmp(Name, "centerD") == 0)
-	{
-		if (i > 299)
-		{
-			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
-			ui.centerD->setValue(Freq_Change(3, i));
-			settings->setValue("Modem/RXFreq4", ui.centerD->value());
-			AGW_Report_Modem_Change(3);
-		}
-		return;
-	}
-
-	if (strcmp(Name, "DCDSlider") == 0)
-	{
-		dcd_threshold = i;
-		saveSettings();
-		return;
-	}
-	
-	if (strcmp(Name, "RXOffset") == 0)
-	{
-		char valChar[32];
-		rxOffset = i;
-		sprintf(valChar, "RX Offset %d",rxOffset);
-		ui.RXOffsetLabel->setText(valChar);
-
-		NeedWaterfallHeaders = true;
-
-		pnt_change[0] = 1;
-		pnt_change[1] = 1;
-		pnt_change[2] = 1;
-		pnt_change[3] = 1;
-
-		saveSettings();
-		return;
-	}
-
-	QMessageBox msgBox;
-	msgBox.setWindowTitle("MessageBox Title");
-	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
-	msgBox.exec();
-}
-
-
-void QtSoundModem::clickedSlot()
-{
-	char Name[32];
-
-	strcpy(Name, sender()->objectName().toUtf8());
-
-	if (strcmp(Name, "actDevices") == 0)
-	{
-		doDevices();
-		return;
-	}
-
-	if (strcmp(Name, "actModems") == 0)
-	{
-		doModems();
-		return;
-	}
-
-	if (strcmp(Name, "showBPF_A") == 0)
-	{
-		doFilter(0, 0);
-		return;
-	}
-
-	if (strcmp(Name, "showTXBPF_A") == 0)
-	{
-		doFilter(0, 1);
-		return;
-	}
-
-	if (strcmp(Name, "showLPF_A") == 0)
-	{
-		doFilter(0, 2);
-		return;
-	}
-	
-
-	if (strcmp(Name, "showBPF_B") == 0)
-	{
-		doFilter(1, 0);
-		return;
-	}
-
-	if (strcmp(Name, "showTXBPF_B") == 0)
-	{
-		doFilter(1, 1);
-		return;
-	}
-
-	if (strcmp(Name, "showLPF_B") == 0)
-	{
-		doFilter(1, 2);
-		return;
-	}
-
-	if (strcmp(Name, "Low_A") == 0)
-	{
-		handleButton(0, 1);
-		return;
-	}
-
-	if (strcmp(Name, "High_A") == 0)
-	{
-		handleButton(0, 2);
-		return;
-	}
-
-	if (strcmp(Name, "Both_A") == 0)
-	{
-		handleButton(0, 3);
-		return;
-	}
-
-	if (strcmp(Name, "Stop_A") == 0)
-	{
-		handleButton(0, 0);
-		return;
-	}
-
-
-	if (strcmp(Name, "Low_B") == 0)
-	{
-		handleButton(1, 1);
-		return;
-	}
-
-	if (strcmp(Name, "High_B") == 0)
-	{
-		handleButton(1, 2);
-		return;
-	}
-
-	if (strcmp(Name, "Both_B") == 0)
-	{
-		handleButton(1, 3);
-		return;
-	}
-
-	if (strcmp(Name, "Stop_B") == 0)
-	{
-		handleButton(1, 0);
-		return;
-	}
-
-	if (strcmp(Name, "Low_C") == 0)
-	{
-		handleButton(2, 1);
-		return;
-	}
-
-	if (strcmp(Name, "High_C") == 0)
-	{
-		handleButton(2, 2);
-		return;
-	}
-
-	if (strcmp(Name, "Both_C") == 0)
-	{
-		handleButton(2, 3);
-		return;
-	}
-
-	if (strcmp(Name, "Stop_C") == 0)
-	{
-		handleButton(2, 0);
-		return;
-	}
-
-	if (strcmp(Name, "Low_D") == 0)
-	{
-		handleButton(3, 1);
-		return;
-	}
-
-	if (strcmp(Name, "High_D") == 0)
-	{
-		handleButton(3, 2);
-		return;
-	}
-
-	if (strcmp(Name, "Both_D") == 0)
-	{
-		handleButton(3, 3);
-		return;
-	}
-
-	if (strcmp(Name, "Stop_D") == 0)
-	{
-		handleButton(3, 0);
-		return;
-	}
-
-	if (strcmp(Name, "actFont") == 0)
-	{
-		bool ok;
-		Font = QFontDialog::getFont(&ok, QFont(Font, this));
-
-		if (ok)
-		{
-			// the user clicked OK and font is set to the font the user selected
-			QApplication::setFont(Font);
-			sessionTable->horizontalHeader()->setFont(Font); 
-
-			saveSettings();
-		}
-		else
-		{
-			// the user canceled the dialog; font is set to the initial
-			// value, in this case Helvetica [Cronyx], 10
-
-//			QApplication::setFont(Font);
-
-		}
-		return;
-	}
-
-	QMessageBox msgBox;
-	msgBox.setWindowTitle("MessageBox Title");
-	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
-	msgBox.exec();
-}
-
-Ui_ModemDialog * Dlg;
-
-QDialog * modemUI;
-QDialog * deviceUI;
-
-void QtSoundModem::doModems()
-{
-	Dlg = new(Ui_ModemDialog);
-
-	QDialog UI;
-	char valChar[10];
-
-	Dlg->setupUi(&UI);
-
-	modemUI = &UI;
-	deviceUI = 0;
-
-	myResize *resize = new myResize();
-
-	UI.installEventFilter(resize);
-
-	sprintf(valChar, "%d", bpf[0]);
-	Dlg->BPFWidthA->setText(valChar);
-	sprintf(valChar, "%d", bpf[1]);
-	Dlg->BPFWidthB->setText(valChar);
-	sprintf(valChar, "%d", bpf[2]);
-	Dlg->BPFWidthC->setText(valChar);
-	sprintf(valChar, "%d", bpf[3]);
-	Dlg->BPFWidthD->setText(valChar);
-
-	sprintf(valChar, "%d", txbpf[0]);
-	Dlg->TXBPFWidthA->setText(valChar);
-	sprintf(valChar, "%d", txbpf[1]);
-	Dlg->TXBPFWidthB->setText(valChar);
-	sprintf(valChar, "%d", txbpf[2]);
-	Dlg->TXBPFWidthC->setText(valChar);
-	sprintf(valChar, "%d", txbpf[3]);
-	Dlg->TXBPFWidthD->setText(valChar);
-
-	sprintf(valChar, "%d", lpf[0]);
-	Dlg->LPFWidthA->setText(valChar);
-	sprintf(valChar, "%d", lpf[1]);
-	Dlg->LPFWidthB->setText(valChar);
-	sprintf(valChar, "%d", lpf[2]);
-	Dlg->LPFWidthC->setText(valChar);
-	sprintf(valChar, "%d", lpf[4]);
-	Dlg->LPFWidthD->setText(valChar);
-
-	sprintf(valChar, "%d", BPF_tap[0]);
-	Dlg->BPFTapsA->setText(valChar);
-	sprintf(valChar, "%d", BPF_tap[1]);
-	Dlg->BPFTapsB->setText(valChar);
-	sprintf(valChar, "%d", BPF_tap[2]);
-	Dlg->BPFTapsC->setText(valChar);
-	sprintf(valChar, "%d", BPF_tap[3]);
-	Dlg->BPFTapsD->setText(valChar);
-
-	sprintf(valChar, "%d", LPF_tap[0]);
-	Dlg->LPFTapsA->setText(valChar);
-	sprintf(valChar, "%d", LPF_tap[1]);
-	Dlg->LPFTapsB->setText(valChar);
-	sprintf(valChar, "%d", LPF_tap[2]);
-	Dlg->LPFTapsC->setText(valChar);
-	sprintf(valChar, "%d", LPF_tap[3]);
-	Dlg->LPFTapsD->setText(valChar);
-
-	Dlg->preEmphAllA->setChecked(emph_all[0]);
-
-	if (emph_all[0])
-		Dlg->preEmphA->setDisabled(TRUE);
-	else
-		Dlg->preEmphA->setCurrentIndex(emph_db[0]);
-
-	Dlg->preEmphAllB->setChecked(emph_all[1]);
-
-	if (emph_all[1])
-		Dlg->preEmphB->setDisabled(TRUE);
-	else
-		Dlg->preEmphB->setCurrentIndex(emph_db[1]);
-
-	Dlg->preEmphAllC->setChecked(emph_all[2]);
-
-	if (emph_all[2])
-		Dlg->preEmphC->setDisabled(TRUE);
-	else
-		Dlg->preEmphC->setCurrentIndex(emph_db[2]);
-
-	Dlg->preEmphAllD->setChecked(emph_all[3]);
-
-	if (emph_all[3])
-		Dlg->preEmphD->setDisabled(TRUE);
-	else
-		Dlg->preEmphD->setCurrentIndex(emph_db[3]);
-
-
-	Dlg->nonAX25A->setChecked(NonAX25[0]);
-	Dlg->nonAX25B->setChecked(NonAX25[1]);
-	Dlg->nonAX25C->setChecked(NonAX25[2]);
-	Dlg->nonAX25D->setChecked(NonAX25[3]);
-
-	Dlg->KISSOptA->setChecked(KISS_opt[0]);
-	Dlg->KISSOptB->setChecked(KISS_opt[1]);
-	Dlg->KISSOptC->setChecked(KISS_opt[2]);
-	Dlg->KISSOptD->setChecked(KISS_opt[3]);
-
-	sprintf(valChar, "%d", maxframe[0]);
-	Dlg->MaxFrameA->setText(valChar);
-	sprintf(valChar, "%d", maxframe[1]);
-	Dlg->MaxFrameB->setText(valChar);
-	sprintf(valChar, "%d", maxframe[2]);
-	Dlg->MaxFrameC->setText(valChar);
-	sprintf(valChar, "%d", maxframe[3]);
-	Dlg->MaxFrameD->setText(valChar);
-
-	sprintf(valChar, "%d", txdelay[0]);
-	Dlg->TXDelayA->setText(valChar);
-	sprintf(valChar, "%d", txdelay[1]);
-	Dlg->TXDelayB->setText(valChar);
-	sprintf(valChar, "%d", txdelay[2]);
-	Dlg->TXDelayC->setText(valChar);
-	sprintf(valChar, "%d", txdelay[3]);
-	Dlg->TXDelayD->setText(valChar);
-
-
-	sprintf(valChar, "%d", txtail[0]);
-	Dlg->TXTailA->setText(valChar);
-	sprintf(valChar, "%d", txtail[1]);
-	Dlg->TXTailB->setText(valChar);
-	sprintf(valChar, "%d", txtail[2]);
-	Dlg->TXTailC->setText(valChar);
-	sprintf(valChar, "%d", txtail[3]);
-	Dlg->TXTailD->setText(valChar);
-
-	Dlg->FrackA->setText(QString::number(frack_time[0]));
-	Dlg->FrackB->setText(QString::number(frack_time[1]));
-	Dlg->FrackC->setText(QString::number(frack_time[2]));
-	Dlg->FrackD->setText(QString::number(frack_time[3]));
-
-	Dlg->RetriesA->setText(QString::number(fracks[0]));
-	Dlg->RetriesB->setText(QString::number(fracks[1]));
-	Dlg->RetriesC->setText(QString::number(fracks[2]));
-	Dlg->RetriesD->setText(QString::number(fracks[3]));
-
-	sprintf(valChar, "%d", RCVR[0]);
-	Dlg->AddRXA->setText(valChar);
-	sprintf(valChar, "%d", RCVR[1]);
-	Dlg->AddRXB->setText(valChar);
-	sprintf(valChar, "%d", RCVR[2]);
-	Dlg->AddRXC->setText(valChar);
-	sprintf(valChar, "%d", RCVR[3]);
-	Dlg->AddRXD->setText(valChar);
-
-	sprintf(valChar, "%d", rcvr_offset[0]);
-	Dlg->RXShiftA->setText(valChar);
-
-	sprintf(valChar, "%d", rcvr_offset[1]);
-	Dlg->RXShiftB->setText(valChar);
-
-	sprintf(valChar, "%d", rcvr_offset[2]);
-	Dlg->RXShiftC->setText(valChar);
-	sprintf(valChar, "%d", rcvr_offset[3]);
-	Dlg->RXShiftD->setText(valChar);
-
-	//	speed[1]
-	//	speed[2];
-
-	Dlg->recoverBitA->setCurrentIndex(recovery[0]);
-	Dlg->recoverBitB->setCurrentIndex(recovery[1]);
-	Dlg->recoverBitC->setCurrentIndex(recovery[2]);
-	Dlg->recoverBitD->setCurrentIndex(recovery[3]);
-
-	Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]);
-	Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]);
-	Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]);
-	Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]);
-
-	Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]);
-	Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]);
-	Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]);
-	Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]);
-
-	Dlg->CRC_A->setChecked(il2p_crc[0]);
-	Dlg->CRC_B->setChecked(il2p_crc[1]);
-	Dlg->CRC_C->setChecked(il2p_crc[2]);
-	Dlg->CRC_D->setChecked(il2p_crc[3]);
-
-	Dlg->CWIDCall->setText(CWIDCall);
-	Dlg->CWIDInterval->setText(QString::number(CWIDInterval));
-	Dlg->CWIDMark->setText(CWIDMark);
-
-	if (CWIDType)
-		Dlg->radioButton_2->setChecked(1);
-	else
-		Dlg->CWIDType->setChecked(1);
-
-	Dlg->afterTraffic->setChecked(afterTraffic);
-
-	Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]);
-	Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]);
-	Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]);
-	Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]);
-
-	Dlg->RSIDUI_A->setChecked(RSID_UI[0]);
-	Dlg->RSIDUI_B->setChecked(RSID_UI[1]);
-	Dlg->RSIDUI_C->setChecked(RSID_UI[2]);
-	Dlg->RSIDUI_D->setChecked(RSID_UI[3]);
-
-	Dlg->DigiCallsA->setText(MyDigiCall[0]);
-	Dlg->DigiCallsB->setText(MyDigiCall[1]);
-	Dlg->DigiCallsC->setText(MyDigiCall[2]);
-	Dlg->DigiCallsD->setText(MyDigiCall[3]);
-
-	Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]);
-	Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]);
-	Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]);
-	Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]);
-	
-	connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-
-	connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-
-	connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-
-	connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-	connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-
-	connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept()));
-	connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave()));
-	connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject()));
-
-	connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA()));
-	connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB()));
-	connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC()));
-	connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD()));
-
-	connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int)));
-	connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int)));
-	connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int)));
-	connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int)));
-
-	UI.exec();
-}
-
-void QtSoundModem::preEmphAllAChanged(int state)
-{
-	Dlg->preEmphA->setDisabled(state);
-}
-
-void QtSoundModem::preEmphAllBChanged(int state)
-{
-	Dlg->preEmphB->setDisabled(state);
-}
-
-void QtSoundModem::preEmphAllCChanged(int state)
-{
-	Dlg->preEmphC->setDisabled(state);
-}
-
-void QtSoundModem::preEmphAllDChanged(int state)
-{
-	Dlg->preEmphD->setDisabled(state);
-}
-
-extern "C" void get_exclude_list(char * line, TStringList * list);
-
-void QtSoundModem::modemaccept()
-{
-	modemSave();
-
-	AGW_Report_Modem_Change(0);
-	AGW_Report_Modem_Change(1);
-	AGW_Report_Modem_Change(2);
-	AGW_Report_Modem_Change(3);
-
-	delete(Dlg);
-	saveSettings();
-
-	modemUI->accept();
-
-}
-
-void QtSoundModem::modemSave()
-{
-	QVariant Q;
-	
-	emph_all[0] = Dlg->preEmphAllA->isChecked();
-	emph_db[0] = Dlg->preEmphA->currentIndex();
-
-	emph_all[1] = Dlg->preEmphAllB->isChecked();
-	emph_db[1] = Dlg->preEmphB->currentIndex();
-
-	emph_all[2] = Dlg->preEmphAllC->isChecked();
-	emph_db[2] = Dlg->preEmphC->currentIndex();
-
-	emph_all[3] = Dlg->preEmphAllD->isChecked();
-	emph_db[3] = Dlg->preEmphD->currentIndex();
-
-	NonAX25[0] = Dlg->nonAX25A->isChecked();
-	NonAX25[1] = Dlg->nonAX25B->isChecked();
-	NonAX25[2] = Dlg->nonAX25C->isChecked();
-	NonAX25[3] = Dlg->nonAX25D->isChecked();
-
-	KISS_opt[0] = Dlg->KISSOptA->isChecked();
-	KISS_opt[1] = Dlg->KISSOptB->isChecked();
-	KISS_opt[2] = Dlg->KISSOptC->isChecked();
-	KISS_opt[3] = Dlg->KISSOptD->isChecked();
-
-	if (emph_db[0] < 0 || emph_db[0] > nr_emph)
-		emph_db[0] = 0;
-
-	if (emph_db[1] < 0 || emph_db[1] > nr_emph)
-		emph_db[1] = 0;
-
-	if (emph_db[2] < 0 || emph_db[2] > nr_emph)
-		emph_db[2] = 0;
-
-	if (emph_db[3] < 0 || emph_db[3] > nr_emph)
-		emph_db[3] = 0;
-
-	Q = Dlg->TXDelayA->text();
-	txdelay[0] = Q.toInt();
-
-	Q = Dlg->TXDelayB->text();
-	txdelay[1] = Q.toInt();
-	
-	Q = Dlg->TXDelayC->text();
-	txdelay[2] = Q.toInt();
-
-	Q = Dlg->TXDelayD->text();
-	txdelay[3] = Q.toInt();
-
-	Q = Dlg->MaxFrameA->text();
-	maxframe[0] = Q.toInt();
-
-	Q = Dlg->MaxFrameB->text();
-	maxframe[1] = Q.toInt();
-
-	Q = Dlg->MaxFrameC->text();
-	maxframe[2] = Q.toInt();
-
-	Q = Dlg->MaxFrameD->text();
-	maxframe[3] = Q.toInt();
-
-	if (maxframe[0] == 0 || maxframe[0] > 7) maxframe[0] = 3;
-	if (maxframe[1] == 0 || maxframe[1] > 7) maxframe[1] = 3;
-	if (maxframe[2] == 0 || maxframe[2] > 7) maxframe[2] = 3;
-	if (maxframe[3] == 0 || maxframe[3] > 7) maxframe[3] = 3;
-
-	Q = Dlg->TXTailA->text();
-	txtail[0] = Q.toInt();
-
-	Q = Dlg->TXTailB->text();
-	txtail[1] = Q.toInt();
-
-	Q = Dlg->TXTailC->text();
-	txtail[2] = Q.toInt();
-
-	txtail[3] = Dlg->TXTailD->text().toInt();
-
-	frack_time[0] = Dlg->FrackA->text().toInt();
-	frack_time[1] = Dlg->FrackB->text().toInt();
-	frack_time[2] = Dlg->FrackC->text().toInt();
-	frack_time[3] = Dlg->FrackD->text().toInt();
-
-	fracks[0] = Dlg->RetriesA->text().toInt();
-	fracks[1] = Dlg->RetriesB->text().toInt();
-	fracks[2] = Dlg->RetriesC->text().toInt();
-	fracks[3] = Dlg->RetriesD->text().toInt();
-
-	Q = Dlg->AddRXA->text();
-	RCVR[0] = Q.toInt();
-
-	Q = Dlg->AddRXB->text();
-	RCVR[1] = Q.toInt();
-
-	Q = Dlg->AddRXC->text();
-	RCVR[2] = Q.toInt();
-
-	Q = Dlg->AddRXD->text();
-	RCVR[3] = Q.toInt();
-
-	Q = Dlg->RXShiftA->text();
-	rcvr_offset[0] = Q.toInt();
-
-	Q = Dlg->RXShiftB->text();
-	rcvr_offset[1] = Q.toInt();
-
-	Q = Dlg->RXShiftC->text();
-	rcvr_offset[2] = Q.toInt();
-
-	Q = Dlg->RXShiftD->text();
-	rcvr_offset[3] = Q.toInt();
-
-	fx25_mode[0] = Dlg->fx25ModeA->currentIndex();
-	fx25_mode[1] = Dlg->fx25ModeB->currentIndex();
-	fx25_mode[2] = Dlg->fx25ModeC->currentIndex();
-	fx25_mode[3] = Dlg->fx25ModeD->currentIndex();
-
-	il2p_mode[0] = Dlg->IL2PModeA->currentIndex();
-	il2p_mode[1] = Dlg->IL2PModeB->currentIndex();
-	il2p_mode[2] = Dlg->IL2PModeC->currentIndex();
-	il2p_mode[3] = Dlg->IL2PModeD->currentIndex();
-
-	il2p_crc[0] = Dlg->CRC_A->isChecked();
-	il2p_crc[1] = Dlg->CRC_B->isChecked();
-	il2p_crc[2] = Dlg->CRC_C->isChecked();
-	il2p_crc[3] = Dlg->CRC_D->isChecked();
-
-	recovery[0] = Dlg->recoverBitA->currentIndex();
-	recovery[1] = Dlg->recoverBitB->currentIndex();
-	recovery[2] = Dlg->recoverBitC->currentIndex();
-	recovery[3] = Dlg->recoverBitD->currentIndex();
-
-
-	strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper());
-	strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper());
-	CWIDInterval = Dlg->CWIDInterval->text().toInt();
-	CWIDType = Dlg->radioButton_2->isChecked();
-
-	afterTraffic = Dlg->afterTraffic->isChecked();
-
-	if (CWIDInterval && afterTraffic == false)
-		cwidtimer->start(CWIDInterval * 60000);
-	else
-		cwidtimer->stop();
-
-
-	RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked();
-	RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked();
-	RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked();
-	RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked();
-
-	RSID_UI[0] = Dlg->RSIDUI_A->isChecked();
-	RSID_UI[1] = Dlg->RSIDUI_B->isChecked();
-	RSID_UI[2] = Dlg->RSIDUI_C->isChecked();
-	RSID_UI[3] = Dlg->RSIDUI_D->isChecked();
-
-	RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked();
-	RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked();
-	RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked();
-	RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked();
-
-	Q = Dlg->DigiCallsA->text();
-	strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper());
-
-	Q = Dlg->DigiCallsB->text();
-	strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper());
-
-	Q = Dlg->DigiCallsC->text();
-	strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper());
-
-	Q = Dlg->DigiCallsD->text();
-	strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper());
-
-	int i;
-
-	for (i = 0; i < 4; i++)
-	{
-		initTStringList(&list_digi_callsigns[i]);
-		get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]);
-	}
-
-/*
-	Q = Dlg->LPFWidthA->text();
-	lpf[0] = Q.toInt();
-
-	Q = Dlg->LPFWidthB->text();
-	lpf[1] = Q.toInt();
-
-	Q = Dlg->LPFWidthC->text();
-	lpf[2] = Q.toInt();
-
-	Q = Dlg->LPFWidthD->text();
-	lpf[3] = Q.toInt();
-*/
-
-}
-
-void QtSoundModem::modemreject()
-{
-	delete(Dlg);
-	modemUI->reject();
-}
-
-void QtSoundModem::doRSIDA()
-{
-	needRSID[0] = 1;
-}
-
-void QtSoundModem::doRSIDB()
-{
-	needRSID[1] = 1;
-}
-
-void QtSoundModem::doRSIDC()
-{
-	needRSID[2] = 1;
-}
-
-void QtSoundModem::doRSIDD()
-{
-	needRSID[3] = 1;
-}
-
-
-
-
-void QtSoundModem::doFilter(int Chan, int Filter)
-{
-	Ui_Dialog Dev;
-	QImage * bitmap;
-
-	QDialog UI;
-
-	Dev.setupUi(&UI);
-
-	bitmap = new QImage(642, 312, QImage::Format_RGB32);
-
-	bitmap->fill(qRgb(255, 255, 255));
-
-	QPainter qPainter(bitmap);
-	qPainter.setBrush(Qt::NoBrush);
-	qPainter.setPen(Qt::black);
-
-	if (Filter == 0)
-		make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter);
-	else if (Filter == 1)
-		make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter);
-	else
-		make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter);
-
-	qPainter.end();
-	Dev.label->setPixmap(QPixmap::fromImage(*bitmap));
-
-	UI.exec();
-
-}
-
-Ui_devicesDialog * Dev;
-
-char NewPTTPort[80];
-
-int newSoundMode = 0;
-int oldSoundMode = 0;
-
-void QtSoundModem::SoundModeChanged(bool State)
-{
-	UNUSED(State);
-
-	// Mustn't change SoundMode until dialog is accepted
-
-	if (Dev->UDP->isChecked())
-		newSoundMode = 3;
-	else if (Dev->PULSE->isChecked())
-		newSoundMode = 2;
-	else
-		newSoundMode = Dev->OSS->isChecked();
-
-}
-
-void QtSoundModem::DualPTTChanged(bool State)
-{
-	UNUSED(State);
-
-	// Forse Evaluation of Cat Port setting
-
-	PTTPortChanged(0);
-}
-
-void QtSoundModem::CATChanged(bool State)
-{
-	UNUSED(State);
-	PTTPortChanged(0);
-}
-
-void QtSoundModem::PTTPortChanged(int Selected)
-{
-	UNUSED(Selected);
-
-	QVariant Q = Dev->PTTPort->currentText();
-	strcpy(NewPTTPort, Q.toString().toUtf8());
-
-	Dev->RTSDTR->setVisible(false);
-	Dev->CAT->setVisible(false);
-
-	Dev->PTTOnLab->setVisible(false);
-	Dev->PTTOn->setVisible(false);
-	Dev->PTTOff->setVisible(false);
-	Dev->PTTOffLab->setVisible(false);
-	Dev->CATLabel->setVisible(false);
-	Dev->CATSpeed->setVisible(false);
-
-	Dev->GPIOLab->setVisible(false);
-	Dev->GPIOLeft->setVisible(false);
-	Dev->GPIORight->setVisible(false);
-	Dev->GPIOLab2->setVisible(false);
-
-	Dev->CM108Label->setVisible(false);
-	Dev->VIDPID->setVisible(false);
-
-	if (strcmp(NewPTTPort, "None") == 0)
-	{
-	}
-	else if (strcmp(NewPTTPort, "GPIO") == 0)
-	{
-		Dev->GPIOLab->setVisible(true);
-		Dev->GPIOLeft->setVisible(true);
-		if (Dev->DualPTT->isChecked())
-		{
-			Dev->GPIORight->setVisible(true);
-			Dev->GPIOLab2->setVisible(true);
-		}
-	}
-
-	else if (strcmp(NewPTTPort, "CM108") == 0)
-	{
-		Dev->CM108Label->setVisible(true);
-//#ifdef __ARM_ARCHX
-		Dev->CM108Label->setText("CM108 Device");
-//#else
-//		Dev->CM108Label->setText("CM108 VID/PID");
-//#endif
-		Dev->VIDPID->setText(CM108Addr);
-		Dev->VIDPID->setVisible(true);
-	}
-	else if (strcmp(NewPTTPort, "HAMLIB") == 0)
-	{
-		Dev->CM108Label->setVisible(true);
-		Dev->CM108Label->setText("rigctrld Port");
-		Dev->VIDPID->setText(QString::number(HamLibPort));
-		Dev->VIDPID->setVisible(true);
-		Dev->PTTOnLab->setText("rigctrld Host");
-		Dev->PTTOnLab->setVisible(true);
-		Dev->PTTOn->setText(HamLibHost);
-		Dev->PTTOn->setVisible(true);
-	}
-	else
-	{
-		Dev->RTSDTR->setVisible(true);
-		Dev->CAT->setVisible(true);
-
-		if (Dev->CAT->isChecked())
-		{
-			Dev->PTTOnLab->setVisible(true);
-			Dev->PTTOnLab->setText("PTT On String");
-			Dev->PTTOn->setText(PTTOnString);
-			Dev->PTTOn->setVisible(true);
-			Dev->PTTOff->setVisible(true);
-			Dev->PTTOff->setText(PTTOffString);
-			Dev->PTTOffLab->setVisible(true);
-			Dev->CATLabel->setVisible(true);
-			Dev->CATSpeed->setVisible(true);
-		}
-	}
-}
-
-bool myResize::eventFilter(QObject *obj, QEvent *event)
-{
-	if (event->type() == QEvent::Resize)
-	{
-		QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
-		QSize size = resizeEvent->size();
-		int h = size.height();
-		int w = size.width();
-
-		if (obj == deviceUI)
-			Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));
-		else
-			Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));
-
-		return true;
-	}
-	return QObject::eventFilter(obj, event);
-}
-
-void QtSoundModem::doDevices()
-{
-	char valChar[10];
-
-	Dev = new(Ui_devicesDialog);
-
-	QDialog UI;
-
-	int i;
-
-	Dev->setupUi(&UI);
-
-	deviceUI = &UI;
-	modemUI = 0;
-
-	myResize *resize = new myResize();
-
-	UI.installEventFilter(resize);
-
-	newSoundMode = SoundMode;
-	oldSoundMode = SoundMode;
-
-#ifdef WIN32
-	Dev->ALSA->setText("WaveOut");
-	Dev->OSS->setVisible(0);
-	Dev->PULSE->setVisible(0);
-#endif
-
-	if (SoundMode == 0)
-		Dev->ALSA->setChecked(1);
-	else if (SoundMode == 1)
-		Dev->OSS->setChecked(1);
-	else if (SoundMode == 2)
-		Dev->PULSE->setChecked(1);
-	else if (SoundMode == 2)
-		Dev->UDP->setChecked(1);
-
-	connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
-	connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
-	connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
-	connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
-
-	for (i = 0; i < PlaybackCount; i++)
-		Dev->outputDevice->addItem(&PlaybackNames[i][0]);
-
-	i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);
-
-
-	if (i == -1)
-	{
-		// Add device to list
-
-		Dev->outputDevice->addItem(PlaybackDevice);
-		i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);
-	}
-
-	Dev->outputDevice->setCurrentIndex(i);
-
-	for (i = 0; i < CaptureCount; i++)
-		Dev->inputDevice->addItem(&CaptureNames[i][0]);
-
-	i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);
-
-	if (i == -1)
-	{
-		// Add device to list
-
-		Dev->inputDevice->addItem(CaptureDevice);
-		i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);
-	}
-	Dev->inputDevice->setCurrentIndex(i);
-
-	Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]);
-	Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]);
-	Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]);
-	Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]);
-
-	// Disable "None" option in first modem
-
-	QStandardItemModel *model = dynamic_cast<QStandardItemModel *>(Dev->Modem_1_Chan->model());
-	QStandardItem * item = model->item(0, 0);
-	item->setEnabled(false);
-
-	Dev->singleChannelOutput->setChecked(SCO);
-	Dev->colourWaterfall->setChecked(raduga);
-
-	sprintf(valChar, "%d", KISSPort);
-	Dev->KISSPort->setText(valChar);
-	Dev->KISSEnabled->setChecked(KISSServ);
-
-	sprintf(valChar, "%d", AGWPort);
-	Dev->AGWPort->setText(valChar);
-	Dev->AGWEnabled->setChecked(AGWServ);
-
-	Dev->PTTOn->setText(PTTOnString);
-	Dev->PTTOff->setText(PTTOffString);
-
-	sprintf(valChar, "%d", PTTBAUD);
-	Dev->CATSpeed->setText(valChar);
-
-	sprintf(valChar, "%d", UDPClientPort);
-	Dev->UDPPort->setText(valChar);
-	Dev->UDPTXHost->setText(UDPHost);
-
-	if (UDPServerPort != TXPort)
-		sprintf(valChar, "%d/%d", UDPServerPort, TXPort);
-	else
-		sprintf(valChar, "%d", UDPServerPort);
-
-	Dev->UDPTXPort->setText(valChar);
-
-	Dev->UDPEnabled->setChecked(UDPServ);
-
-	sprintf(valChar, "%d", pttGPIOPin);
-	Dev->GPIOLeft->setText(valChar);
-	sprintf(valChar, "%d", pttGPIOPinR);
-	Dev->GPIORight->setText(valChar);
-
-	Dev->VIDPID->setText(CM108Addr);
-
-	QStringList items;
-
-	connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool)));
-	connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool)));
-	connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));
-
-	if (PTTMode == PTTCAT)
-		Dev->CAT->setChecked(true);
-	else
-		Dev->RTSDTR->setChecked(true);
-
-	for (const QSerialPortInfo &info : Ports)
-	{
-		items.append(info.portName());
-	}
-
-	items.sort();
-
-	Dev->PTTPort->addItem("None");
-	Dev->PTTPort->addItem("CM108");
-
-	//#ifdef __ARM_ARCH
-
-	Dev->PTTPort->addItem("GPIO");
-
-	//#endif
-
-	Dev->PTTPort->addItem("HAMLIB");
-
-	for (const QString &info : items)
-	{
-		Dev->PTTPort->addItem(info);
-	}
-
-	// If we are using a user specifed device add it
-
-	i = Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString);
-
-	if (i == -1)
-	{
-		// Add our device to list
-
-		Dev->PTTPort->insertItem(0, PTTPort);
-		i = Dev->PTTPort->findText(PTTPort, Qt::MatchContains);
-	}
-
-	Dev->PTTPort->setCurrentIndex(i);
-
-	PTTPortChanged(0);				// Force reevaluation
-
-	Dev->txRotation->setChecked(TX_rotate);
-	Dev->DualPTT->setChecked(DualPTT);
-
-	Dev->multiCore->setChecked(multiCore);
-	Dev->darkTheme->setChecked(darkTheme);
-
-	Dev->WaterfallMin->setCurrentIndex(Dev->WaterfallMin->findText(QString::number(WaterfallMin), Qt::MatchFixedString));
-	Dev->WaterfallMax->setCurrentIndex(Dev->WaterfallMax->findText(QString::number(WaterfallMax), Qt::MatchFixedString));
-
-	QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept()));
-	QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject()));
-
-	UI.exec();
-
-}
-
-void QtSoundModem::mysetstyle()
-{
-	if (darkTheme)
-	{
-		qApp->setStyleSheet(
-			"QWidget {color: white; background-color: black}"
-			"QTabBar::tab {color: rgb(127, 127, 127); background-color: black}"
-			"QTabBar::tab::selected {color: white}"
-			"QPushButton {border-style: outset; border-width: 2px; border-color: rgb(127, 127, 127)}"
-			"QPushButton::default {border-style: outset; border-width: 2px; border-color: white}");
-
-		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(40, 40, 40) }");
-
-		txText = qRgb(255, 127, 127);
-		rxText = qRgb(173, 216, 230);
-	}
-	else
-	{
-		qApp->setStyleSheet("");
-
-		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }");
-
-		txText = qRgb(192, 0, 0);
-		rxText = qRgb(0, 0, 192);
-	}
-}
-
-void QtSoundModem::deviceaccept()
-{
-	QVariant Q = Dev->inputDevice->currentText();
-	int cardChanged = 0;
-	char portString[32];
-	int newMax;
-	int newMin;
-
-	if (Dev->UDP->isChecked())
-	{
-		// cant have server and slave
-
-		if (Dev->UDPEnabled->isChecked())
-		{
-			QMessageBox::about(this, tr("QtSoundModem"),
-				tr("Can't have UDP sound source and UDP server at same time"));
-			return;
-		}
-	}
-
-	if (oldSoundMode != newSoundMode)
-	{
-		QMessageBox msgBox;
-
-		msgBox.setText("QtSoundModem must restart to change Sound Mode.\n"
-			"Program will close if you hit Ok\n"
-			"You will need to reselect audio devices after restarting");
-
-		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
-
-		int i = msgBox.exec();
-
-		if (i == QMessageBox::Ok)
-		{			
-			SoundMode = newSoundMode;
-			saveSettings();
-
-			Closing = 1;
-			return;
-		}
-
-		if (oldSoundMode == 0)
-			Dev->ALSA->setChecked(1);
-		else if (oldSoundMode == 1)
-			Dev->OSS->setChecked(1);
-		else if (oldSoundMode == 2)
-			Dev->PULSE->setChecked(1);
-		else if (oldSoundMode == 3)
-			Dev->UDP->setChecked(1);
-
-		QMessageBox::about(this, tr("Info"),
-			tr("<p align = 'center'>Changes not saved</p>"));
-
-		return;
-
-	}
-
-	if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0)
-	{
-		strcpy(CaptureDevice, Q.toString().toUtf8());
-		cardChanged = 1;
-	}
-
-	CaptureIndex = Dev->inputDevice->currentIndex();
-
-	Q = Dev->outputDevice->currentText();
-
-	if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0)
-	{
-		strcpy(PlaybackDevice, Q.toString().toUtf8());
-		cardChanged = 1;
-	}
-
-	PlayBackIndex = Dev->outputDevice->currentIndex();
-
-	soundChannel[0] = Dev->Modem_1_Chan->currentIndex();
-	soundChannel[1] = Dev->Modem_2_Chan->currentIndex();
-	soundChannel[2] = Dev->Modem_3_Chan->currentIndex();
-	soundChannel[3] = Dev->Modem_4_Chan->currentIndex();
-
-	UsingLeft = 0;
-	UsingRight = 0;
-	UsingBothChannels = 0;
-
-	for (int i = 0; i < 4; i++)
-	{
-		if (soundChannel[i] == LEFT)
-		{
-			UsingLeft = 1;
-			modemtoSoundLR[i] = 0;
-		}
-		else if (soundChannel[i] == RIGHT)
-		{
-			UsingRight = 1;
-			modemtoSoundLR[i] = 1;
-		}
-	}
-
-	if (UsingLeft && UsingRight)
-		UsingBothChannels = 1;
-
-	SCO = Dev->singleChannelOutput->isChecked();
-	raduga = Dev->colourWaterfall->isChecked();
-	AGWServ = Dev->AGWEnabled->isChecked();
-	KISSServ = Dev->KISSEnabled->isChecked();
-
-	Q = Dev->KISSPort->text();
-	KISSPort = Q.toInt();
-
-	Q = Dev->AGWPort->text();
-	AGWPort = Q.toInt();
-
-	Q = Dev->PTTPort->currentText();
-	
-	char temp[256];
-
-	strcpy(temp, Q.toString().toUtf8());
-
-	if (strlen(temp))
-		strcpy(PTTPort, temp);
-
-	DualPTT = Dev->DualPTT->isChecked();
-	TX_rotate = Dev->txRotation->isChecked();
-	multiCore = Dev->multiCore->isChecked();
-	darkTheme = Dev->darkTheme->isChecked();
-	mysetstyle();
-
-	if (Dev->CAT->isChecked())
-		PTTMode = PTTCAT;
-	else
-		PTTMode = PTTRTS;
-
-	Q = Dev->PTTOn->text();
-	strcpy(PTTOnString, Q.toString().toUtf8());
-	Q = Dev->PTTOff->text();
-	strcpy(PTTOffString, Q.toString().toUtf8());
-
-	Q = Dev->CATSpeed->text();
-	PTTBAUD = Q.toInt();
-
-	Q = Dev->UDPPort->text();
-	UDPClientPort = Q.toInt();
-
-
-	Q = Dev->UDPTXPort->text();
-	strcpy(portString, Q.toString().toUtf8());
-	UDPServerPort = atoi(portString);
-
-	if (strchr(portString, '/'))
-	{
-		char * ptr = strlop(portString, '/');
-		TXPort = atoi(ptr);
-	}
-	else
-		TXPort = UDPServerPort;
-
-	Q = Dev->UDPTXHost->text();
-	strcpy(UDPHost, Q.toString().toUtf8());
-
-	UDPServ = Dev->UDPEnabled->isChecked();
-
-	Q = Dev->GPIOLeft->text();
-	pttGPIOPin = Q.toInt();
-
-	Q = Dev->GPIORight->text();
-	pttGPIOPinR = Q.toInt();
-
-	Q = Dev->VIDPID->text();
-
-	if (strcmp(PTTPort, "CM108") == 0)
-		strcpy(CM108Addr, Q.toString().toUtf8());
-	else if (strcmp(PTTPort, "HAMLIB") == 0)
-	{
-		HamLibPort = Q.toInt();
-		Q = Dev->PTTOn->text();
-		strcpy(HamLibHost, Q.toString().toUtf8());
-	}
-
-	Q = Dev->WaterfallMax->currentText();
-	newMax = Q.toInt();
-
-	Q = Dev->WaterfallMin->currentText();
-	newMin = Q.toInt();
-
-	if (newMax != WaterfallMax || newMin != WaterfallMin)
-	{
-		QMessageBox msgBox;
-
-		msgBox.setText("QtSoundModem must restart to change Waterfall range. Program will close if you hit Ok\n"
-		"It may take up to 30 seconds for the program to start for the first time after changing settings");
-
-		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
-
-		int i = msgBox.exec();
-
-		if (i == QMessageBox::Ok)
-		{
-			Configuring = 1;			// Stop Waterfall
-
-			WaterfallMax = newMax;
-			WaterfallMin = newMin;
-			saveSettings();
-			Closing = 1;
-			return;
-		}
-	}
-
-
-	ClosePTTPort();
-	OpenPTTPort();
-
-	NeedWaterfallHeaders = true;
-
-	delete(Dev);
-	saveSettings();
-	deviceUI->accept();
-
-	if (cardChanged)
-	{
-		InitSound(1);
-	}
-
-	// Reset title and tooltip in case ports changed 
-
-	char Title[128];
-	sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : "");
-	w->setWindowTitle(Title);
-
-	sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort);
-	if (trayIcon)
-		trayIcon->setToolTip(Title);
-
-	QSize newSize(this->size());
-	QSize oldSize(this->size());
-
-	QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize);
-
-	QCoreApplication::postEvent(this, myResizeEvent);  
-}
-
-void QtSoundModem::devicereject()
-{	
-	delete(Dev);
-	deviceUI->reject();
-}
-
-void QtSoundModem::handleButton(int Port, int Type)
-{
-	// interlock calib with CWID
-
-	if (calib_mode[0] == 4)		// CWID
-		return;
-	
-	doCalib(Port, Type);
-}
-
-
-
-void QtSoundModem::doRestartWF()
-{
-	if (inWaterfall)
-	{
-		// in waterfall update thread
-
-		wftimer->start(5000);
-		return;
-	}
-		
-	lockWaterfall = true;
-
-	if (Firstwaterfall | Secondwaterfall)
-	{
-		initWaterfall(0);
-		initWaterfall(1);
-	}
-
-	delete(RXLevel);
-	delete(ui.RXLevel);
-	ui.RXLevel = new QLabel(ui.centralWidget);
-	RXLevelCopy = ui.RXLevel;
-	ui.RXLevel->setGeometry(QRect(780, 14, 150, 11));
-	ui.RXLevel->setFrameShape(QFrame::Box);
-	ui.RXLevel->setFrameShadow(QFrame::Sunken);
-
-	delete(RXLevel2);
-	delete(ui.RXLevel2);
-
-	ui.RXLevel2 = new QLabel(ui.centralWidget);
-	RXLevel2Copy = ui.RXLevel2;
-
-	ui.RXLevel2->setGeometry(QRect(780, 23, 150, 11));
-	ui.RXLevel2->setFrameShape(QFrame::Box);
-	ui.RXLevel2->setFrameShadow(QFrame::Sunken);
-
-	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
-	RXLevel->fill(cyan);
-
-	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
-	RXLevel2->fill(white);
-
-	ui.RXLevel->setVisible(1);
-	ui.RXLevel2->setVisible(1);
-
-	lockWaterfall = false;
-}
-
-
-void QtSoundModem::doAbout()
-{
-	QMessageBox::about(this, tr("About"),
-		tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO"));
-}
-
-void QtSoundModem::doCalibrate()
-{
-	Ui_calDialog Calibrate;
-	{
-		QDialog UI;
-		Calibrate.setupUi(&UI);
-
-		connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-		connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot()));
-
-		/*
-		
-		connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); });
-		connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); });
-		connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); });
-		connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); });
-		connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); });
-		connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); });
-		connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); });
-		connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); });
-
-//		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2)));
-*/
-		UI.exec();
-	}
-}
-
-void QtSoundModem::RefreshSpectrum(unsigned char * Data)
-{
-	int i;
-
-	// Last 4 bytes are level busy and Tuning lines
-
-	Waterfall->fill(Black);
-
-	if (Data[206] != LastLevel)
-	{
-		LastLevel = Data[206];
-//		RefreshLevel(LastLevel);
-	}
-
-	if (Data[207] != LastBusy)
-	{
-		LastBusy = Data[207];
-//		Busy->setVisible(LastBusy);
-	}
-
-	for (i = 0; i < 205; i++)
-	{
-		int val = Data[0];
-
-		if (val > 63)
-			val = 63;
-
-		Waterfall->setPixel(i, val, Yellow);
-		if (val < 62)
-			Waterfall->setPixel(i, val + 1, Gold);
-		Data++;
-	}
-
-	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));
-
-}
-
-void RefreshLevel(unsigned int Level, unsigned int LevelR)
-{
-	// Redraw the RX Level Bar Graph
-
-	unsigned int  x, y;
-
-	for (x = 0; x < 150; x++)
-	{
-		for (y = 0; y < 10; y++)
-		{
-			if (x < Level)
-			{
-				if (Level < 50)
-					RXLevel->setPixel(x, y, yellow);
-				else if (Level > 135)
-					RXLevel->setPixel(x, y, red);
-				else
-					RXLevel->setPixel(x, y, green);
-			}
-			else
-				RXLevel->setPixel(x, y, white);
-		}
-	}
-	RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel));
-
-	for (x = 0; x < 150; x++)
-	{
-		for (y = 0; y < 10; y++)
-		{
-			if (x < LevelR)
-			{
-				if (LevelR < 50)
-					RXLevel2->setPixel(x, y, yellow);
-				else if (LevelR > 135)
-					RXLevel2->setPixel(x, y, red);
-				else
-					RXLevel2->setPixel(x, y, green);
-			}
-			else
-				RXLevel2->setPixel(x, y, white);
-		}
-	}
-	RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2));
-}
-
-extern "C" unsigned char CurrentLevel;
-extern "C" unsigned char CurrentLevelR;
-
-void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data)
-{
-	int j;
-	unsigned char * Line;
-	int len = Waterfall->bytesPerLine();
-	int TopLine = NextWaterfallLine[snd_ch];
-
-	// Write line to cyclic buffer then draw starting with the line just written
-
-	// Length is 208 bytes, including Level and Busy flags
-
-	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206);
-	if (NextWaterfallLine[snd_ch] > 63)
-		NextWaterfallLine[snd_ch] = 0;
-
-	for (j = 63; j > 0; j--)
-	{
-		Line = Waterfall->scanLine(j);
-		memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len);
-		if (TopLine > 63)
-			TopLine = 0;
-	}
-
-	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));
-}
-
-void QtSoundModem::sendtoTrace(char * Msg, int tx)
-{
-	const QTextCursor old_cursor = monWindowCopy->textCursor();
-	const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value();
-	const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum();
-
-	// Move the cursor to the end of the document.
-	monWindowCopy->moveCursor(QTextCursor::End);
-
-	// Insert the text at the position of the cursor (which is the end of the document).
-
-	if (tx)
-		monWindowCopy->setTextColor(txText);
-	else
-		monWindowCopy->setTextColor(rxText);
-
-	monWindowCopy->textCursor().insertText(Msg);
-
-	if (old_cursor.hasSelection() || !is_scrolled_down)
-	{
-		// The user has selected text or scrolled away from the bottom: maintain position.
-		monWindowCopy->setTextCursor(old_cursor);
-		monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value);
-	}
-	else
-	{
-		// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
-		monWindowCopy->moveCursor(QTextCursor::End);
-		monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum());
-	}
-
-	free(Msg);
-}
-
-
-// I think this does the waterfall
-
-typedef struct TRGBQ_t
-{
-	Byte b, g, r, re;
-
-} TRGBWQ;
-
-typedef struct tagRECT
-{
-	int    left;
-	int    top;
-	int    right;
-	int    bottom;
-} RECT;
-
-unsigned int RGBWF[256] ;
-
-
-extern "C" void init_raduga()
-{
-	Byte offset[6] = {0, 51, 102, 153, 204};
-	Byte i, n;
-
-	for (n = 0; n < 52; n++)
-	{
-		i = n * 5;
-
-		RGBWF[n + offset[0]] = qRgb(0, 0, i);
-		RGBWF[n + offset[1]] = qRgb(0, i, 255);
-		RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i);
-		RGBWF[n + offset[3]] = qRgb(1, 255, 0);
-		RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0);
-	}
-}
-
-extern "C" int nonGUIMode;
-
-
-// This draws the Frequency Scale on Waterfall
-
-extern "C" void DrawFreqTicks()
-{
-	if (nonGUIMode)
-		return;
-
-	// Draw Frequency Markers on waterfall header(s);
-
-	int x, i;
-	char Textxx[20];
-	QImage * bm = Waterfall;
-
-	QPainter qPainter(bm);
-	qPainter.setBrush(Qt::black);
-	qPainter.setPen(Qt::white);
-
-	int Chan;
-
-#ifdef WIN32
-		int Top = 3;
-#else
-		int Top = 4;
-#endif
-		int Base = 0;
-
-		for (Chan = 0; Chan < 2; Chan++)
-		{
-			if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
-				sprintf(Textxx, "Right");
-			else
-				sprintf(Textxx, "Left");
-
-			qPainter.drawText(2, Top, 100, 20, 0, Textxx);
-
-			// We drew markers every 100 Hz or 100 / binsize pixels
-
-			int Markers = ((WaterfallMax - WaterfallMin) / 100) + 5;			// Number of Markers to draw
-			int Freq = WaterfallMin;
-			float PixelsPerMarker = 100.0 / BinSize;
-
-			for (i = 0; i < Markers; i++)
-			{
-				x = round(PixelsPerMarker * i);
-				if (x < 1025)
-				{
-					if ((Freq % 500) == 0)
-						qPainter.drawLine(x, Base + 22, x, Base + 15);
-					else
-						qPainter.drawLine(x, Base + 22, x, Base + 18);
-
-					if ((Freq % 500) == 0)
-					{
-						sprintf(Textxx, "%d", Freq);
-
-						if (x < 924)
-							qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx);
-					}
-				}
-				Freq += 100;
-			}
-
-			if (UsingBothChannels == 0)
-				break;
-
-			Top += WaterfallTotalPixels;
-			Base = WaterfallTotalPixels;
-		}
-
-}
-
-// These draws the frequency Markers on the Waterfall
-
-void DrawModemFreqRange()
-{
-	if (nonGUIMode)
-		return;
-
-	// Draws the modem freq bars on waterfall header(s)
-
-
-	int x1, x2, k, pos1, pos2, pos3;
-	QImage * bm = Waterfall;
-
-	QPainter qPainter(bm);
-	qPainter.setBrush(Qt::NoBrush);
-	qPainter.setPen(Qt::white);
-
-	int Chan;
-	int LRtoDisplay = LEFT;
-	int top = 0;
-
-	for (Chan = 0; Chan < 2; Chan++)
-	{
-		if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
-			LRtoDisplay = RIGHT;
-
-		//	bm->fill(black);
-
-	//	qPainter.fillRect(top, 23, 1024, 10, Qt::black);
-
-		// We drew markers every 100 Hz or 100 / binsize pixels
-
-		float PixelsPerHz = 1.0 / BinSize;
-		k = 26 + top;
-
-		// draw all enabled ports on the ports on this soundcard
-
-		// First Modem is always on the first waterfall
-		// If second is enabled it is on the first unless different
-		//		channel from first
-
-		for (int i = 0; i < 4; i++)
-		{
-			if (soundChannel[i] != LRtoDisplay)
-					continue;
-
-			pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
-			pos2 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
-			pos3 = roundf(((rxOffset + chanOffset[i] + rx_freq[i])) - WaterfallMin * PixelsPerHz);
-			x1 = pos1 + 5;
-			x2 = pos2 + 5;
-
-			qPainter.setPen(Qt::white);
-			qPainter.drawLine(x1, k, x2, k);
-			qPainter.drawLine(x1, k - 3, x1, k + 3);
-			qPainter.drawLine(x2, k - 3, x2, k + 3);
-			//		qPainter.drawLine(pos3, k - 3, pos3, k + 3);
-
-			if (rxOffset || chanOffset[i])
-			{
-				// Draw TX posn if rxOffset used
-
-				pos3 = roundf((rx_freq[i] - WaterfallMin) * PixelsPerHz);
-				qPainter.setPen(Qt::magenta);
-				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
-				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
-				qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3);
-			}
-
-			k += 3;
-		}
-		if (UsingBothChannels == 0)
-			break;
-
-		LRtoDisplay = RIGHT;
-		top = WaterfallTotalPixels;
-	}
-}
-
-
-void doWaterfallThread(void * param);
-
-extern "C" void doWaterfall(int snd_ch)
-{
-	if (nonGUIMode)
-		return;
-
-	if (Closing)
-		return;
-
-	if (lockWaterfall)
-		return;
-
-//	if (multiCore)			// Run modems in separate threads
-//		_beginthread(doWaterfallThread, 0, xx);
-//	else
-		doWaterfallThread((void *)(size_t)snd_ch);
-
-}
-
-extern "C" void displayWaterfall()
-{
-	// if we are using both channels but only want right need to extract correct half of Image
-
-	if (Waterfall == nullptr)
-		return;
-
-	if (UsingBothChannels && (Firstwaterfall == 0))
-		WaterfallCopy->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
-	else
-		WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft);
-
-	WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall));
-}
-
-extern "C" float aFFTAmpl[1024];
-extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag);
-
-void doWaterfallThread(void * param)
-{
-	int snd_ch = (int)(size_t)param;
-
-	if (lockWaterfall)
-		return;
-
-	if (Configuring)
-		return;
-
-	inWaterfall = true;					// don't allow restart waterfall
-
-	if (snd_ch == 1 && UsingLeft == 0)	// Only using right
-		snd_ch = 0;						// Samples are in first buffer
-
-	QImage * bm = Waterfall;
-
-	int  i;
-	single  mag;
-	UCHAR * p;
-	UCHAR Line[4096] = "";			// 4 bytes per pixel
-
-	int lineLen, Start, End;
-	word  hFFTSize;
-	Byte  n;
-	float RealOut[8192] = { 0 };
-	float ImagOut[8192];
-
-
-	RefreshLevel(CurrentLevel, CurrentLevelR);	// Signal Level
-
-	hFFTSize = FFTSize / 2;
-
-
-	// I think an FFT should produce n/2 bins, each of Samp/n Hz
-	// Looks like my code only works with n a power of 2
-
-	// So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel 
-	// display (is this enough?)
-
-	Start = (WaterfallMin / BinSize);		// First and last bins to process
-	End = (WaterfallMax / BinSize);
-
-	if (0)	//RSID_WF
-	{
-		// Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE];
-
-		for (i = 0; i < hFFTSize; i++)
-		{
-			mag = aFFTAmpl[i];
-
-			mag *= 0.00000042f;
-
-			if (mag < 0.00001f)
-				mag = 0.00001f;
-
-			if (mag > 1.0f)
-				mag = 1.0f;
-
-			mag = 22 * log2f(mag) + 255;
-
-			if (mag < 0)
-				mag = 0;
-
-			fft_disp[snd_ch][i] = round(mag);
-		}
-	}
-	else
-	{
-		dofft(&fft_buf[snd_ch][0], RealOut, ImagOut);
-
-		//	FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0);
-
-		for (i = Start; i < End; i++)
-		{
-			//mag: = ComplexMag(fft_d[i])*0.00000042;
-
-	//		mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f;
-
-			mag = powf(RealOut[i], 2);
-			mag += powf(ImagOut[i], 2);
-			mag = sqrtf(mag);
-			mag *= 0.00000042f;
-
-
-			if (mag > MaxMagOut)
-			{
-				MaxMagOut = mag;
-				MaxMagIndex = i;
-			}
-
-			if (mag < 0.00001f)
-				mag = 0.00001f;
-
-			if (mag > 1.0f)
-				mag = 1.0f;
-
-			mag = 22 * log2f(mag) + 255;
-
-			if (mag < 0)
-				mag = 0;
-
-			MagOut[i] = mag;					// for Freq Guess
-			fft_disp[snd_ch][i] = round(mag);
-		}
-	}
-
-	SMUpdateBusyDetector(snd_ch, RealOut, ImagOut);
-
-
-
-	// we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display
-
-	if (bm == 0)
-	{
-		inWaterfall = false;
-		return;
-	}
-	if ((Firstwaterfall == 0 && snd_ch == 0) || (Secondwaterfall == 0 && snd_ch == 1))
-	{
-		inWaterfall = false;
-		return;
-	}
-
-	p = Line;
-	lineLen = 4096;
-
-	if (raduga == DISP_MONO)
-	{
-		for (i = Start; i < End; i++)
-		{
-			n = fft_disp[snd_ch][i];
-			*(p++) = n;					// all colours the same
-			*(p++) = n;
-			*(p++) = n;
-			p++;
-		}
-	}
-	else
-	{
-		for (i = Start; i < End; i++)
-		{
-			n = fft_disp[snd_ch][i];
-			memcpy(p, &RGBWF[n], 4);
-			p += 4;
-		}
-	}
-
-	// Scroll
-
-
-
-	int TopLine = NextWaterfallLine[snd_ch];
-	int TopScanLine = WaterfallHeaderPixels;
-
-	if (snd_ch)
-		TopScanLine += WaterfallTotalPixels;
-
-	// Write line to cyclic buffer then draw starting with the line just written
-
-	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096);
-	if (NextWaterfallLine[snd_ch] > 79)
-		NextWaterfallLine[snd_ch] = 0;
-	
-	// Sanity check
-
-	int WFMaxLine = bm->height();
-
-
-	if ((79 + TopScanLine) >= bm->height())
-	{
-		printf("Invalid WFMaxLine %d \n", bm->height());
-			exit(1);
-	}
-
-
-	for (int j = 79; j > 0; j--)
-	{
-		p = bm->scanLine(j + TopScanLine);
-		if (p == nullptr)
-		{
-			printf("Invalid WF Pointer \n");
-			exit(1);
-		}
-		memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen);
-		TopLine++;
-		if (TopLine > 79)
-			TopLine = 0;
-	}
-
-	inWaterfall = false;
-}
-
-
-void QtSoundModem::changeEvent(QEvent* e)
-{
-	if (e->type() == QEvent::WindowStateChange)
-	{
-		QWindowStateChangeEvent* ev = static_cast<QWindowStateChangeEvent*>(e);
-
-		qDebug() << windowState();
-
-		if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized)
-		{
-			if (trayIcon)
-				setVisible(false);
-		}
-//		if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState)
-//		{
-//			QMessageBox::information(this, "", "Window has been restored");
-//		}
-
-	}
-	QWidget::changeEvent(e);
-}
-
-#include <QCloseEvent>
-
-void QtSoundModem::closeEvent(QCloseEvent *event)
-{
-	UNUSED(event);
-
-	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
-	mysettings.setValue("geometry", QWidget::saveGeometry());
-	mysettings.setValue("windowState", saveState());
-
-	Closing = TRUE;
-	qDebug() << "Closing";
-
-	QThread::msleep(100);
-}
-
-	
-QtSoundModem::~QtSoundModem()
-{
-	qDebug() << "Saving Settings";
-		
-	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
-	mysettings.setValue("geometry", saveGeometry());
-	mysettings.setValue("windowState", saveState());
-	
-	saveSettings();	
-	Closing = TRUE;
-	qDebug() << "Closing";
-
-	QThread::msleep(100);
-}
-
-extern "C" void QSleep(int ms)
-{
-	QThread::msleep(ms);
-}
-
-int upd_time = 30;
-
-void QtSoundModem::show_grid()
-{
-	// This refeshes the session list
-
-	int  snd_ch, i, num_rows, row_idx;
-	QTableWidgetItem *item;
-	const char * msg;
-
-	int  speed_tx, speed_rx;
-
-	if (grid_time < 10)
-	{
-		grid_time++;
-		return;
-	}
-
-	grid_time = 0;
-
-	//label7.Caption = inttostr(stat_r_mem); mem_arq
-
-	num_rows = 0;
-	row_idx = 0;
-
-	for (snd_ch = 0; snd_ch < 4; snd_ch++)
-	{
-		for (i = 0; i < port_num; i++)
-		{
-			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
-				num_rows++;
-		}
-	}
-
-	if (num_rows == 0)
-	{
-		sessionTable->clearContents();
-		sessionTable->setRowCount(0);
-		sessionTable->setRowCount(1);
-	}
-	else
-		sessionTable->setRowCount(num_rows);
-
-
-	for (snd_ch = 0; snd_ch < 4; snd_ch++)
-	{
-		for (i = 0; i < port_num; i++)
-		{
-			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
-			{
-				switch (AX25Port[snd_ch][i].status)
-				{
-				case STAT_NO_LINK:
-
-					msg = "No link";
-					break;
-
-				case STAT_LINK:
-
-					msg = "Link";
-					break;
-
-				case STAT_CHK_LINK:
-
-					msg = "Chk link";
-					break;
-
-				case STAT_WAIT_ANS:
-
-					msg = "Wait ack";
-					break;
-
-				case STAT_TRY_LINK:
-
-					msg = "Try link";
-					break;
-
-				case STAT_TRY_UNLINK:
-
-					msg = "Try unlink";
-				}
-
-
-				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall);
-				sessionTable->setItem(row_idx, 0, item);
-
-				item = new QTableWidgetItem(AX25Port[snd_ch][i].kind);
-				sessionTable->setItem(row_idx, 11, item);
-
-				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall);
-				sessionTable->setItem(row_idx, 1, item);
-
-				item = new QTableWidgetItem(msg);
-				sessionTable->setItem(row_idx, 2, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt));
-				sessionTable->setItem(row_idx, 3, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte));
-				sessionTable->setItem(row_idx, 4, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt));
-				sessionTable->setItem(row_idx, 5, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte));
-				sessionTable->setItem(row_idx, 6, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc));
-				sessionTable->setItem(row_idx, 7, item);
-
-				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count));
-				sessionTable->setItem(row_idx, 8, item);
-
-				if (grid_timer != upd_time)
-					grid_timer++;
-				else
-				{
-					grid_timer = 0;
-					speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time);
-					speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time);
-
-					item = new QTableWidgetItem(QString::number(speed_tx));
-					sessionTable->setItem(row_idx, 9, item);
-
-					item = new QTableWidgetItem(QString::number(speed_rx));
-					sessionTable->setItem(row_idx, 10, item);
-
-					AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte;
-					AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte;	
-				}
-
-				row_idx++;
-			}
-		}
-	}
-}
-
-// "Copy on Select" Code
-
-void QtSoundModem::onTEselectionChanged()
-{
-	QTextEdit * x = static_cast<QTextEdit*>(QObject::sender());
-	x->copy();
-}
-
-#define ConstellationHeight 121
-#define ConstellationWidth 121
-#define PLOTRADIUS 60
-
-#define MAX(x, y) ((x) > (y) ? (x) : (y))
-
-extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count)
-{
-	// Subroutine to update bmpConstellation plot for PSK modes...
-	// Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014)
-
-	float dblPhaseError;
-	float dblPhaseErrorSum = 0;
-	float intP = 0;
-	float dblRad = 0;
-	float dblAvgRad = 0;
-	float dbPhaseStep;
-	float MagMax = 0;
-	float dblPlotRotation = 0;
-
-	int i, intQuality;
-
-	int intX, intY;
-	int yCenter = (ConstellationHeight - 2) / 2;
-	int xCenter = (ConstellationWidth - 2) / 2;
-
-	if (Count == 0)
-		return 0;
-
-	if (nonGUIMode == 0)
-	{
-		Constellation[chan]->fill(black);
-
-		for (i = 0; i < 120; i++)
-		{
-			Constellation[chan]->setPixel(xCenter, i, cyan);
-			Constellation[chan]->setPixel(i, xCenter, cyan);
-		}
-	}
-	dbPhaseStep = 2 * M_PI / intPSKPhase;
-
-	for (i = 1; i < Count; i++)  // Don't plot the first phase (reference)
-	{
-		MagMax = MAX(MagMax, Mags[i]); // find the max magnitude to auto scale
-		dblAvgRad += Mags[i];
-	}
-
-	dblAvgRad = dblAvgRad / Count; // the average radius
-
-	for (i = 0; i < Count; i++)
-	{
-		dblRad = PLOTRADIUS * Mags[i] / MagMax; //  scale the radius dblRad based on intMag
-		intP = round((Phases[i]) / dbPhaseStep);
-
-		// compute the Phase error
-
-		dblPhaseError = fabsf(Phases[i] - intP * dbPhaseStep); // always positive and < .5 *  dblPhaseStep
-		dblPhaseErrorSum += dblPhaseError;
-
-		if (nonGUIMode == 0)
-		{
-			intX = xCenter + dblRad * cosf(dblPlotRotation + Phases[i]);
-			intY = yCenter + dblRad * sinf(dblPlotRotation + Phases[i]);
-
-			if (intX > 0 && intY > 0)
-				if (intX != xCenter && intY != yCenter)
-					Constellation[chan]->setPixel(intX, intY, yellow);
-		}
-	}
-
-	dblAvgRad = dblAvgRad / Count; // the average radius
-
-	intQuality = MAX(0, ((100 - 200 * (dblPhaseErrorSum / (Count)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM
-
-	if (nonGUIMode == 0)
-	{
-		char QualText[64];
-		sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality);
-		QualLabel[chan]->setText(QualText);
-		constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan]));
-	}
-	return intQuality;
-}
-
-
-
+/*
+Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
+
+This file is part of QtSoundModem
+
+QtSoundModem is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QtSoundModem is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with QtSoundModem.  If not, see http://www.gnu.org/licenses
+
+*/
+
+// UZ7HO Soundmodem Port by John Wiseman G8BPQ
+
+// UZ7HO Soundmodem Port
+
+// Not Working 4psk100 FEC 
+
+// Thoughts on Waterfall Display.
+
+// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum
+
+// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size?
+
+// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273
+
+#include "QtSoundModem.h"
+#include <qheaderview.h>
+//#include <QDebug>
+#include <QHostAddress>
+#include <QAbstractSocket>
+#include <QSettings>
+#include <QPainter>
+#include <QtSerialPort/QSerialPort>
+#include <QtSerialPort/QSerialPortInfo>
+#include <QMessageBox>
+#include <QTimer>
+#include <qevent.h>
+#include <QStandardItemModel>
+#include <QScrollBar>
+#include <QFontDialog>
+#include "UZ7HOStuff.h"
+
+
+QImage *Constellation[4];
+QImage *Waterfall = 0;
+QLabel *DCDLabel[4];
+QLineEdit *chanOffsetLabel[4];
+QImage *DCDLed[4];
+
+QImage *RXLevel;
+QImage *RXLevel2;
+
+QLabel *WaterfallCopy;
+
+QLabel * RXLevelCopy;
+QLabel * RXLevel2Copy;
+
+QTextEdit * monWindowCopy;
+
+extern workerThread *t;
+extern QtSoundModem * w;
+extern QCoreApplication * a;
+
+QList<QSerialPortInfo> Ports = QSerialPortInfo::availablePorts();
+
+void saveSettings();
+void getSettings();
+void DrawModemFreqRange();
+
+extern "C" void CloseSound();
+extern "C" void GetSoundDevices();
+extern "C" char modes_name[modes_count][21];
+extern "C" int speed[5];
+extern "C" int KISSPort;
+extern "C" short rx_freq[5];
+
+extern "C" int CaptureCount;
+extern "C" int PlaybackCount;
+
+extern "C" int CaptureIndex;		// Card number
+extern "C" int PlayBackIndex;
+
+extern "C" char CaptureNames[16][256];
+extern "C" char PlaybackNames[16][256];
+
+extern "C" int SoundMode;
+extern "C" int multiCore;
+
+extern "C" int refreshModems;
+int NeedPSKRefresh;
+
+extern "C" int pnt_change[5];
+extern "C" int needRSID[4];
+
+extern "C" int needSetOffset[4];
+
+extern "C" float MagOut[4096];
+extern "C" float MaxMagOut;
+extern "C" int MaxMagIndex;
+
+
+extern "C" int using48000;			// Set if using 48K sample rate (ie RUH Modem active)
+extern "C" int ReceiveSize;
+extern "C" int SendSize;		// 100 mS for now
+
+extern "C"
+{ 
+	int InitSound(BOOL Report);
+	void soundMain();
+	void MainLoop();
+	void modulator(UCHAR snd_ch, int buf_size);
+	void SampleSink(int LR, short Sample);
+	void doCalib(int Port, int Act);
+	int Freq_Change(int Chan, int Freq);
+	void set_speed(int snd_ch, int Modem);
+	void init_speed(int snd_ch);
+	void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform);
+	void dofft(short * in, float * outr, float * outi);
+	void init_raduga();
+	void DrawFreqTicks();
+	void AGW_Report_Modem_Change(int port);
+	char * strlop(char * buf, char delim);
+	void sendRSID(int Chan, int dropTX);
+	void RSIDinitfft();
+	void il2p_init(int il2p_debug);
+}
+
+void make_graph_buf(float * buf, short tap, QPainter * bitmap);
+
+int ModemA = 2;
+int ModemB = 2;
+int ModemC = 2;
+int ModemD = 2;
+int FreqA = 1500;
+int FreqB = 1500;
+int FreqC = 1500;
+int FreqD = 1500;
+int DCD = 50;
+
+char CWIDCall[128] = "";
+extern "C" char CWIDMark[32] = "";
+int CWIDInterval = 0;
+int CWIDLeft = 0;
+int CWIDRight = 0;
+int CWIDType = 1;			// on/off
+bool afterTraffic = 0;
+bool cwidtimerisActive = false;
+
+int WaterfallMin = 00;
+int WaterfallMax = 6000;
+
+int Configuring = 0;
+bool lockWaterfall = false;
+bool inWaterfall = false;
+
+extern "C" int NeedWaterfallHeaders;
+extern "C" float BinSize;
+
+extern "C" { int RSID_SABM[4]; }
+extern "C" { int RSID_UI[4]; }
+extern "C" { int RSID_SetModem[4]; }
+extern "C" unsigned int pskStates[4];
+
+int Closing = FALSE;				// Set to stop background thread
+
+QRgb white = qRgb(255, 255, 255);
+QRgb black = qRgb(0, 0, 0);
+
+QRgb green = qRgb(0, 255, 0);
+QRgb red = qRgb(255, 0, 0);
+QRgb yellow = qRgb(255, 255, 0);
+QRgb cyan = qRgb(0, 255, 255);
+
+QRgb txText = qRgb(192, 0, 0);
+QRgb rxText = qRgb(0, 0, 192);
+
+bool darkTheme = true;
+bool minimizeonStart = true;
+
+// Indexed colour list from ARDOPC
+
+#define WHITE 0
+#define Tomato 1
+#define Gold 2
+#define Lime 3
+#define Yellow 4
+#define Orange 5
+#define Khaki 6
+#define Cyan 7
+#define DeepSkyBlue 8
+#define RoyalBlue 9
+#define Navy 10
+#define Black 11
+#define Goldenrod 12
+#define Fuchsia 13
+
+QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0),
+						qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255),
+						qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0),
+						qRgb(218, 165, 32), qRgb(255, 0, 255) };
+
+unsigned char  WaterfallLines[2][80][4096] = { 0 };
+int NextWaterfallLine[2] = {0, 0};
+
+unsigned int LastLevel = 255;
+unsigned int LastBusy = 255;
+
+extern "C" int UDPClientPort;
+extern "C" int UDPServerPort;
+extern "C" int TXPort;
+extern char UDPHost[64];
+
+QTimer *cwidtimer;
+QWidget * mythis;
+
+
+QSystemTrayIcon * trayIcon = nullptr;
+
+int MintoTray = 1;
+
+int RSID_WF = 0;				// Set to use RSID FFT for Waterfall. 
+
+extern "C" void WriteDebugLog(char * Mess)
+{
+	qDebug() << Mess;
+}
+
+void QtSoundModem::doupdateDCD(int Chan, int State)
+{
+	DCDLabel[Chan]->setVisible(State);
+}
+
+extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat);
+extern "C" char * ShortDateTime();
+
+extern "C" void mon_rsid(int snd_ch, char * RSID)
+{
+	int Len;
+	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot
+
+	sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R');
+
+	Len = strlen(Msg);
+
+	if (Msg[Len - 1] != '\r')
+	{
+		Msg[Len++] = '\r';
+		Msg[Len] = 0;
+	}
+
+	emit t->sendtoTrace(Msg, 0);
+}
+
+extern "C" void put_frame(int snd_ch, string * frame, char * code, int  tx, int excluded)
+{
+	UNUSED(excluded);
+
+	int Len;
+	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot
+
+	if (strcmp(code, "NON-AX25") == 0)
+		sprintf(Msg, "%d: <NON-AX25 frame Len = %d [%s%c]\r", snd_ch, frame->Length, ShortDateTime(), 'R');
+	else
+		sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx));
+
+	Len = strlen(Msg);
+
+	if (Msg[Len - 1] != '\r')
+	{
+		Msg[Len++] = '\r';
+		Msg[Len] = 0;
+	}
+
+	emit t->sendtoTrace(Msg, tx);
+}
+
+extern "C" void updateDCD(int Chan, bool State)
+{
+	emit t->updateDCD(Chan, State);
+}
+
+bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt)
+{
+	UNUSED(obj);
+
+	if (evt->type() == QEvent::Resize)
+	{
+		return QWidget::event(evt);
+	}
+
+	if (evt->type() == QEvent::WindowStateChange)
+	{
+		if (windowState().testFlag(Qt::WindowMinimized) == true)
+			w_state = WIN_MINIMIZED;
+		else
+			w_state = WIN_MAXIMIZED;
+	}
+//	if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma;
+//	{
+//		qDebug() << "App State changed =" << evt->type() << endl;
+//	}
+
+	return QWidget::event(evt);
+}
+
+void QtSoundModem::resizeEvent(QResizeEvent* event)
+{
+	QMainWindow::resizeEvent(event);
+
+	QRect r = geometry();
+
+	QRect r1 = ui.monWindow->geometry();
+	QRect r2 = ui.centralWidget->geometry();
+
+	int modemBoxHeight = 34;
+	
+	int Width = r.width();
+	int Height = r.height() - 25;
+
+	int monitorTop;
+	int monitorHeight;
+	int sessionTop;
+	int sessionHeight = 0;
+	int waterfallsTop;
+	int waterfallsHeight = WaterfallImageHeight;
+
+	ui.modeB->setVisible(soundChannel[1]);
+	ui.centerB->setVisible(soundChannel[1]);
+	ui.labelB->setVisible(soundChannel[1]);
+	DCDLabel[1]->setVisible(soundChannel[1]);
+	ui.RXOffsetB->setVisible(soundChannel[1]);
+
+	ui.modeC->setVisible(soundChannel[2]);
+	ui.centerC->setVisible(soundChannel[2]);
+	ui.labelC->setVisible(soundChannel[2]);
+	DCDLabel[2]->setVisible(soundChannel[2]);
+	ui.RXOffsetC->setVisible(soundChannel[2]);
+
+	ui.modeD->setVisible(soundChannel[3]);
+	ui.centerD->setVisible(soundChannel[3]);
+	ui.labelD->setVisible(soundChannel[3]);
+	DCDLabel[3]->setVisible(soundChannel[3]);
+	ui.RXOffsetD->setVisible(soundChannel[3]);
+
+	if (soundChannel[2] || soundChannel[3])
+		modemBoxHeight = 60;
+
+	ui.Waterfall->setVisible(0);
+
+	monitorTop = modemBoxHeight + 1;
+
+	// Now have one waterfall label containing headers and waterfalls
+
+		if (Firstwaterfall || Secondwaterfall)
+			ui.Waterfall->setVisible(1);
+
+	if (AGWServ)
+	{
+		sessionTable->setVisible(true);
+		sessionHeight = 150;
+	}
+	else
+	{
+		sessionTable->setVisible(false);
+	}
+
+	// if only displaying one Waterfall, change height of waterfall area
+
+	if (UsingBothChannels == 0  || (Firstwaterfall == 0) || (Secondwaterfall == 0))
+	{
+		waterfallsHeight /= 2;
+	}
+
+	if ((Firstwaterfall == 0) && (Secondwaterfall == 0))
+		waterfallsHeight = 0;
+
+	monitorHeight = Height - sessionHeight - waterfallsHeight - modemBoxHeight;
+	waterfallsTop = Height - waterfallsHeight;
+	sessionTop = Height - (sessionHeight + waterfallsHeight);
+
+	ui.monWindow->setGeometry(QRect(0, monitorTop, Width, monitorHeight));
+
+	if (AGWServ)
+		sessionTable->setGeometry(QRect(0, sessionTop, Width, sessionHeight));
+
+	if (waterfallsHeight)
+		ui.Waterfall->setGeometry(QRect(0, waterfallsTop, Width, waterfallsHeight + 2));
+}
+
+QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State)
+{
+	QAction * Act = new QAction(Label, parent);
+	Menu->addAction(Act);
+
+	Act->setCheckable(true);
+	if (State)
+		Act->setChecked(true);
+
+	parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked()));
+
+	return Act;
+}
+
+void QtSoundModem::menuChecked()
+{
+	QAction * Act = static_cast<QAction*>(QObject::sender());
+
+	int state = Act->isChecked();
+
+	if (Act == actWaterfall1)
+		Firstwaterfall = state;
+
+	else if (Act == actWaterfall2)
+		Secondwaterfall = state;
+
+	initWaterfall(Firstwaterfall | Secondwaterfall);
+
+	saveSettings();
+}
+
+void QtSoundModem::initWaterfall(int state)
+{
+	if (state == 1)
+	{
+	//	if (ui.Waterfall)
+	//	{
+	//		delete ui.Waterfall;
+	//		ui.Waterfall = new QLabel(ui.centralWidget);
+	//	}
+		WaterfallCopy = ui.Waterfall;
+
+		Waterfall = new QImage(1024, WaterfallImageHeight + 2, QImage::Format_RGB32);
+
+		NeedWaterfallHeaders = 1;
+
+	}
+	else
+	{
+		delete(Waterfall);
+		Waterfall = 0;
+	}
+
+	QSize Size(800, 602);						// Not actually used, but Event constructor needs it
+
+	QResizeEvent *event = new QResizeEvent(Size, Size);
+	QApplication::sendEvent(this, event);
+}
+
+QRect PSKRect = { 100,100,100,100 };
+
+QDialog * constellationDialog;
+QLabel * constellationLabel[4];
+QLabel * QualLabel[4];
+
+// Local copies
+
+QLabel *RXOffsetLabel;
+QSlider *RXOffset;
+
+QFont Font;
+
+extern "C" void CheckPSKWindows()
+{
+	NeedPSKRefresh = 1;
+}
+void DoPSKWindows()
+{
+	// Display Constellation for PSK Window;
+
+	int NextX = 0;
+	int i;
+
+	for (i = 0; i < 4; i++)
+	{
+		if (soundChannel[i] && pskStates[i])
+		{
+			constellationLabel[i]->setGeometry(QRect(NextX, 19, 121, 121));
+			QualLabel[i]->setGeometry(QRect(1 + NextX, 1, 120, 15));
+			constellationLabel[i]->setVisible(1);
+			QualLabel[i]->setVisible(1);
+
+			NextX += 122;
+		}
+		else
+		{
+			constellationLabel[i]->setVisible(0);
+			QualLabel[i]->setVisible(0);
+		}
+	}
+	constellationDialog->resize(NextX, 140);
+}
+
+QTimer *wftimer;
+
+QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
+{
+	QString family;
+	int csize;
+	QFont::Weight weight;
+
+	ui.setupUi(this);
+
+	mythis = this;
+
+	getSettings();
+
+	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
+
+	family = mysettings.value("FontFamily", "Courier New").toString();
+	csize = mysettings.value("PointSize", 0).toInt();
+	weight = (QFont::Weight)mysettings.value("Weight", 50).toInt();
+
+	Font = QFont(family);
+	Font.setPointSize(csize);
+	Font.setWeight(weight);
+
+	QApplication::setFont(Font);
+
+	constellationDialog = new QDialog(nullptr, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
+	constellationDialog->resize(488, 140);
+	constellationDialog->setGeometry(PSKRect);
+
+	QFont f("Arial", 8, QFont::Normal);
+
+	for (int i = 0; i < 4; i++)
+	{
+		char Text[16];
+		sprintf(Text, "Chan %c", i + 'A');
+
+		constellationLabel[i] = new QLabel(constellationDialog);
+		constellationDialog->setWindowTitle("PSK Constellations");
+
+		QualLabel[i] = new QLabel(constellationDialog);
+		QualLabel[i]->setText(Text);
+		QualLabel[i]->setFont(f);
+
+		Constellation[i] = new QImage(121, 121, QImage::Format_RGB32);
+		Constellation[i]->fill(black);
+		constellationLabel[i]->setPixmap(QPixmap::fromImage(*Constellation[i]));
+	}
+	constellationDialog->show();
+
+	if (MintoTray)
+	{
+		char popUp[256];
+		sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort);
+		trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this);
+		trayIcon->setToolTip(popUp);
+		trayIcon->show();
+		
+		connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason)));
+	}
+
+	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
+	ReceiveSize = 512;
+	SendSize = 1024;		// 100 mS for now
+
+	for (int i = 0; i < 4; i++)
+	{
+		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
+		{
+			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
+			ReceiveSize = 2048;
+			SendSize = 4096;		// 100 mS for now
+		}
+	}
+
+	float FFTCalc = 12000.0f / ((WaterfallMax - WaterfallMin) / 900.0f);
+
+	FFTSize = FFTCalc + 0.4999;
+
+	if (FFTSize > 8191)
+		FFTSize = 8190;
+
+	if (FFTSize & 1)		// odd
+		FFTSize--;
+
+	BinSize = 12000.0 / FFTSize;
+
+	restoreGeometry(mysettings.value("geometry").toByteArray());
+	restoreState(mysettings.value("windowState").toByteArray());
+
+	sessionTable = new QTableWidget(ui.centralWidget);
+
+	sessionTable->verticalHeader()->setVisible(FALSE);
+	sessionTable->verticalHeader()->setDefaultSectionSize(20);
+	sessionTable->horizontalHeader()->setDefaultSectionSize(68);
+	sessionTable->setRowCount(1);
+	sessionTable->setColumnCount(12);
+	m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction";
+
+	mysetstyle();
+
+	sessionTable->setHorizontalHeaderLabels(m_TableHeader);
+	sessionTable->setColumnWidth(0, 80);
+	sessionTable->setColumnWidth(1, 80);
+	sessionTable->setColumnWidth(4, 76);
+	sessionTable->setColumnWidth(5, 76);
+	sessionTable->setColumnWidth(6, 80);
+	sessionTable->setColumnWidth(11, 72);
+
+	for (int i = 0; i < modes_count; i++)
+	{
+		ui.modeA->addItem(modes_name[i]);
+		ui.modeB->addItem(modes_name[i]);
+		ui.modeC->addItem(modes_name[i]);
+		ui.modeD->addItem(modes_name[i]);
+	}
+
+	// Set up Menus
+
+	setupMenu = ui.menuBar->addMenu(tr("Settings"));
+
+	actDevices = new QAction("Setup Devices", this);
+	setupMenu->addAction(actDevices);
+
+	connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot()));
+	actDevices->setObjectName("actDevices");
+	actModems = new QAction("Setup Modems", this);
+	actModems->setObjectName("actModems");
+	setupMenu->addAction(actModems);
+
+	connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot()));
+
+	actFont = new QAction("Setup Font", this);
+	actFont->setObjectName("actFont");
+	setupMenu->addAction(actFont);
+
+	connect(actFont, SIGNAL(triggered()), this, SLOT(clickedSlot()));
+
+	actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray()));
+	actMintoTray->setCheckable(1);
+	actMintoTray->setChecked(MintoTray);
+
+	viewMenu = ui.menuBar->addMenu(tr("&View"));
+
+	actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall);
+	actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall);
+
+	actCalib = ui.menuBar->addAction("&Calibration");
+	connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate()));
+
+	actRestartWF = ui.menuBar->addAction("Restart Waterfall");
+	connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF()));
+
+	actAbout = ui.menuBar->addAction("&About");
+	connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout()));
+
+	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
+	RXLevel->fill(white);
+	ui.RXLevel->setPixmap(QPixmap::fromImage(*RXLevel));
+	RXLevelCopy = ui.RXLevel;
+
+	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
+	RXLevel2->fill(white);
+	ui.RXLevel2->setPixmap(QPixmap::fromImage(*RXLevel2));
+	RXLevel2Copy = ui.RXLevel2;
+
+	DCDLabel[0] = new QLabel(this);
+	DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA"));
+	DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12));
+	DCDLabel[0]->setVisible(TRUE);
+
+	DCDLabel[1] = new QLabel(this);
+	DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB"));
+	DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12));
+	DCDLabel[1]->setVisible(TRUE);
+
+	DCDLabel[2] = new QLabel(this);
+	DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC"));
+	DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12));
+	DCDLabel[2]->setVisible(FALSE);
+
+	DCDLabel[3] = new QLabel(this);
+	DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD"));
+	DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12));
+	DCDLabel[3]->setVisible(FALSE);
+	
+	DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32);
+	DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32);
+	DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32);
+	DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32);
+
+	DCDLed[0]->fill(red);
+	DCDLed[1]->fill(red);
+	DCDLed[2]->fill(red);
+	DCDLed[3]->fill(red);
+
+	DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0]));
+	DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1]));
+	DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2]));
+	DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3]));
+
+	chanOffsetLabel[0] = ui.RXOffsetA;
+	chanOffsetLabel[1] = ui.RXOffsetB;
+	chanOffsetLabel[2] = ui.RXOffsetC;
+	chanOffsetLabel[3] = ui.RXOffsetD;
+
+	WaterfallCopy = ui.Waterfall;
+
+	initWaterfall(Firstwaterfall | Secondwaterfall);
+
+	monWindowCopy = ui.monWindow;
+
+	ui.monWindow->document()->setMaximumBlockCount(10000);
+
+//	connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged()));
+
+	connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
+
+	ModemA = speed[0];
+	ModemB = speed[1];
+	ModemC = speed[2];
+	ModemD = speed[3];
+
+	ui.modeA->setCurrentIndex(speed[0]);
+	ui.modeB->setCurrentIndex(speed[1]);
+	ui.modeC->setCurrentIndex(speed[2]);
+	ui.modeD->setCurrentIndex(speed[3]);
+
+	ModemA = ui.modeA->currentIndex();
+
+	ui.centerA->setValue(rx_freq[0]);
+	ui.centerB->setValue(rx_freq[1]);
+	ui.centerC->setValue(rx_freq[2]);
+	ui.centerD->setValue(rx_freq[3]);
+
+	connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
+
+	ui.DCDSlider->setValue(dcd_threshold);
+
+	char valChar[32];
+	sprintf(valChar, "RX Offset %d", rxOffset);
+	ui.RXOffsetLabel->setText(valChar);
+	ui.RXOffset->setValue(rxOffset);
+
+	RXOffsetLabel = ui.RXOffsetLabel;
+	RXOffset = ui.RXOffset;
+
+	connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int)));
+	connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
+
+	QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection);
+	QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection);
+
+	QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), Qt::QueuedConnection);
+
+	connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+	connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+	connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+	connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+
+	QTimer *timer = new QTimer(this);
+	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
+	timer->start(100);
+
+	wftimer = new QTimer(this);
+	connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF()));
+	wftimer->start(1000 * 300);
+
+	cwidtimer = new QTimer(this);
+	connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer()));
+
+
+	if (CWIDInterval && afterTraffic == false)
+		cwidtimer->start(CWIDInterval * 60000);
+
+	if (RSID_SetModem[0])
+	{
+		RSID_WF = 1;
+		RSIDinitfft();
+	}
+//	il2p_init(1);
+
+	QTimer::singleShot(200, this, &QtSoundModem::updateFont);
+
+}
+
+void QtSoundModem::updateFont()
+{
+	QApplication::setFont(Font);
+}
+
+void QtSoundModem::MinimizetoTray()
+{
+	MintoTray = actMintoTray->isChecked();
+	saveSettings();
+	QMessageBox::about(this, tr("QtSoundModem"),
+	tr("Program must be restarted to change Minimize mode"));
+}
+
+
+void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason)
+{
+	if (reason == 3)
+	{
+		showNormal();
+		w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
+	} 
+}
+
+extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan);
+
+extern "C" void checkforCWID()
+{
+	emit(t->startCWIDTimer());
+};
+
+extern "C" void QtSoundModem::startCWIDTimerSlot()
+{
+	if (CWIDInterval && afterTraffic == 1 && cwidtimerisActive == false)
+	{
+		cwidtimerisActive = true;
+		QTimer::singleShot(CWIDInterval * 60000, this, &QtSoundModem::CWIDTimer);
+	}
+}
+
+void QtSoundModem::CWIDTimer()
+{
+	cwidtimerisActive = false;
+	sendCWID(CWIDCall, CWIDType, 0);
+	calib_mode[0] = 4;
+}
+
+void extSetOffset(int chan)
+{
+	char valChar[32];
+	sprintf(valChar, "%d", chanOffset[chan]);
+	chanOffsetLabel[chan]->setText(valChar);
+
+	NeedWaterfallHeaders = true;
+
+	pnt_change[0] = 1;
+	pnt_change[1] = 1;
+	pnt_change[2] = 1;
+	pnt_change[3] = 1;
+
+	return;
+}
+	
+void QtSoundModem::MyTimerSlot()
+{
+	// 100 mS Timer Event
+
+	for (int i = 0; i < 4; i++)
+	{
+
+		if (needSetOffset[i])
+		{
+			needSetOffset[i] = 0;
+			extSetOffset(i);						// Update GUI
+		}
+	}
+
+	if (refreshModems)
+	{
+		refreshModems = 0;
+
+		ui.modeA->setCurrentIndex(speed[0]);
+		ui.modeB->setCurrentIndex(speed[1]);
+		ui.modeC->setCurrentIndex(speed[2]);
+		ui.modeD->setCurrentIndex(speed[3]);
+		ui.centerA->setValue(rx_freq[0]);
+		ui.centerB->setValue(rx_freq[1]);
+		ui.centerC->setValue(rx_freq[2]);
+		ui.centerD->setValue(rx_freq[3]);
+	}
+	if (NeedPSKRefresh)
+	{
+		NeedPSKRefresh = 0;
+		DoPSKWindows();
+	}
+
+	if (NeedWaterfallHeaders)
+	{
+		NeedWaterfallHeaders = 0;
+
+		if (Waterfall)
+		{
+			Waterfall->fill(black);
+			DrawModemFreqRange();
+			DrawFreqTicks();
+		}
+	}
+
+	show_grid();
+}
+
+void QtSoundModem::returnPressed()
+{
+	char Name[32];
+	int Chan;
+	QString val;
+	
+	strcpy(Name, sender()->objectName().toUtf8());
+
+	Chan = Name[8] - 'A';
+
+	val = chanOffsetLabel[Chan]->text();
+
+	chanOffset[Chan] = val.toInt();
+	needSetOffset[Chan] = 1;				// Update GUI
+
+
+}
+
+void CheckforChanges(int Mode, int OldMode)
+{
+	int old48000 = using48000;
+
+	if (OldMode != Mode && Mode == 15)
+	{
+		QMessageBox msgBox;
+
+		msgBox.setText("Warning!!\nARDOP Packet is NOT the same as ARDOP\n"
+			"It is an experimental mode for sending ax.25 frames using ARDOP packet formats\n");
+
+		msgBox.setStandardButtons(QMessageBox::Ok);
+
+		msgBox.exec();
+	}
+
+	// See if need to switch beween 12000 and 48000
+
+	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
+	ReceiveSize = 512;
+	SendSize = 1024;		// 100 mS for now
+
+	for (int i = 0; i < 4; i++)
+	{
+		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
+		{
+			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
+			ReceiveSize = 2048;
+			SendSize = 4096;		// 100 mS for now
+		}
+	}
+
+	if (using48000 != old48000)
+	{
+		InitSound(1);
+	}
+}
+
+
+void QtSoundModem::clickedSlotI(int i)
+{
+	char Name[32];
+
+	strcpy(Name, sender()->objectName().toUtf8());
+
+	if (strcmp(Name, "modeA") == 0)
+	{
+		int OldModem = ModemA;
+		ModemA = ui.modeA->currentIndex();
+		set_speed(0, ModemA);
+		CheckforChanges(ModemA, OldModem);
+		saveSettings();
+		AGW_Report_Modem_Change(0);
+		return;
+	}
+
+	if (strcmp(Name, "modeB") == 0)
+	{
+		int OldModem = ModemB;
+		ModemB = ui.modeB->currentIndex();
+		set_speed(1, ModemB);
+		CheckforChanges(ModemB, OldModem);
+		saveSettings();
+		AGW_Report_Modem_Change(1);
+		return;
+	}
+
+	if (strcmp(Name, "modeC") == 0)
+	{
+		int OldModem = ModemC;
+		ModemC = ui.modeC->currentIndex();
+		set_speed(2, ModemC);
+		CheckforChanges(ModemC, OldModem);
+		saveSettings();
+		AGW_Report_Modem_Change(2);
+		return;
+	}
+
+	if (strcmp(Name, "modeD") == 0)
+	{
+		int OldModem = ModemD;
+		ModemD = ui.modeD->currentIndex();
+		set_speed(3, ModemD);
+		CheckforChanges(ModemD, OldModem);
+		saveSettings();
+		AGW_Report_Modem_Change(3);
+		return;
+	}
+
+	if (strcmp(Name, "centerA") == 0)
+	{
+		if (i > 299)
+		{
+			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+			ui.centerA->setValue(Freq_Change(0, i));
+			settings->setValue("Modem/RXFreq1", ui.centerA->value());
+			AGW_Report_Modem_Change(0);
+
+		}
+		return;
+	}
+
+	if (strcmp(Name, "centerB") == 0)
+	{
+		if (i > 299)
+		{
+			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+			ui.centerB->setValue(Freq_Change(1, i));
+			settings->setValue("Modem/RXFreq2", ui.centerB->value());
+			AGW_Report_Modem_Change(1);
+		}
+		return;
+	}
+
+	if (strcmp(Name, "centerC") == 0)
+	{
+		if (i > 299)
+		{
+			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+			ui.centerC->setValue(Freq_Change(2, i));
+			settings->setValue("Modem/RXFreq3", ui.centerC->value());
+			AGW_Report_Modem_Change(2);
+		}
+		return;
+	}
+
+	if (strcmp(Name, "centerD") == 0)
+	{
+		if (i > 299)
+		{
+			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
+			ui.centerD->setValue(Freq_Change(3, i));
+			settings->setValue("Modem/RXFreq4", ui.centerD->value());
+			AGW_Report_Modem_Change(3);
+		}
+		return;
+	}
+
+	if (strcmp(Name, "DCDSlider") == 0)
+	{
+		dcd_threshold = i;
+		saveSettings();
+		return;
+	}
+	
+	if (strcmp(Name, "RXOffset") == 0)
+	{
+		char valChar[32];
+		rxOffset = i;
+		sprintf(valChar, "RX Offset %d",rxOffset);
+		ui.RXOffsetLabel->setText(valChar);
+
+		NeedWaterfallHeaders = true;
+
+		pnt_change[0] = 1;
+		pnt_change[1] = 1;
+		pnt_change[2] = 1;
+		pnt_change[3] = 1;
+
+		saveSettings();
+		return;
+	}
+
+	QMessageBox msgBox;
+	msgBox.setWindowTitle("MessageBox Title");
+	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
+	msgBox.exec();
+}
+
+
+void QtSoundModem::clickedSlot()
+{
+	char Name[32];
+
+	strcpy(Name, sender()->objectName().toUtf8());
+
+	if (strcmp(Name, "actDevices") == 0)
+	{
+		doDevices();
+		return;
+	}
+
+	if (strcmp(Name, "actModems") == 0)
+	{
+		doModems();
+		return;
+	}
+
+	if (strcmp(Name, "showBPF_A") == 0)
+	{
+		doFilter(0, 0);
+		return;
+	}
+
+	if (strcmp(Name, "showTXBPF_A") == 0)
+	{
+		doFilter(0, 1);
+		return;
+	}
+
+	if (strcmp(Name, "showLPF_A") == 0)
+	{
+		doFilter(0, 2);
+		return;
+	}
+	
+
+	if (strcmp(Name, "showBPF_B") == 0)
+	{
+		doFilter(1, 0);
+		return;
+	}
+
+	if (strcmp(Name, "showTXBPF_B") == 0)
+	{
+		doFilter(1, 1);
+		return;
+	}
+
+	if (strcmp(Name, "showLPF_B") == 0)
+	{
+		doFilter(1, 2);
+		return;
+	}
+
+	if (strcmp(Name, "Low_A") == 0)
+	{
+		handleButton(0, 1);
+		return;
+	}
+
+	if (strcmp(Name, "High_A") == 0)
+	{
+		handleButton(0, 2);
+		return;
+	}
+
+	if (strcmp(Name, "Both_A") == 0)
+	{
+		handleButton(0, 3);
+		return;
+	}
+
+	if (strcmp(Name, "Stop_A") == 0)
+	{
+		handleButton(0, 0);
+		return;
+	}
+
+
+	if (strcmp(Name, "Low_B") == 0)
+	{
+		handleButton(1, 1);
+		return;
+	}
+
+	if (strcmp(Name, "High_B") == 0)
+	{
+		handleButton(1, 2);
+		return;
+	}
+
+	if (strcmp(Name, "Both_B") == 0)
+	{
+		handleButton(1, 3);
+		return;
+	}
+
+	if (strcmp(Name, "Stop_B") == 0)
+	{
+		handleButton(1, 0);
+		return;
+	}
+
+	if (strcmp(Name, "Low_C") == 0)
+	{
+		handleButton(2, 1);
+		return;
+	}
+
+	if (strcmp(Name, "High_C") == 0)
+	{
+		handleButton(2, 2);
+		return;
+	}
+
+	if (strcmp(Name, "Both_C") == 0)
+	{
+		handleButton(2, 3);
+		return;
+	}
+
+	if (strcmp(Name, "Stop_C") == 0)
+	{
+		handleButton(2, 0);
+		return;
+	}
+
+	if (strcmp(Name, "Low_D") == 0)
+	{
+		handleButton(3, 1);
+		return;
+	}
+
+	if (strcmp(Name, "High_D") == 0)
+	{
+		handleButton(3, 2);
+		return;
+	}
+
+	if (strcmp(Name, "Both_D") == 0)
+	{
+		handleButton(3, 3);
+		return;
+	}
+
+	if (strcmp(Name, "Stop_D") == 0)
+	{
+		handleButton(3, 0);
+		return;
+	}
+
+	if (strcmp(Name, "actFont") == 0)
+	{
+		bool ok;
+		Font = QFontDialog::getFont(&ok, QFont(Font, this));
+
+		if (ok)
+		{
+			// the user clicked OK and font is set to the font the user selected
+			QApplication::setFont(Font);
+			sessionTable->horizontalHeader()->setFont(Font); 
+
+			saveSettings();
+		}
+		else
+		{
+			// the user canceled the dialog; font is set to the initial
+			// value, in this case Helvetica [Cronyx], 10
+
+//			QApplication::setFont(Font);
+
+		}
+		return;
+	}
+
+	QMessageBox msgBox;
+	msgBox.setWindowTitle("MessageBox Title");
+	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
+	msgBox.exec();
+}
+
+Ui_ModemDialog * Dlg;
+
+QDialog * modemUI;
+QDialog * deviceUI;
+
+void QtSoundModem::doModems()
+{
+	Dlg = new(Ui_ModemDialog);
+
+	QDialog UI;
+	char valChar[10];
+
+	Dlg->setupUi(&UI);
+
+	modemUI = &UI;
+	deviceUI = 0;
+
+	myResize *resize = new myResize();
+
+	UI.installEventFilter(resize);
+
+	sprintf(valChar, "%d", bpf[0]);
+	Dlg->BPFWidthA->setText(valChar);
+	sprintf(valChar, "%d", bpf[1]);
+	Dlg->BPFWidthB->setText(valChar);
+	sprintf(valChar, "%d", bpf[2]);
+	Dlg->BPFWidthC->setText(valChar);
+	sprintf(valChar, "%d", bpf[3]);
+	Dlg->BPFWidthD->setText(valChar);
+
+	sprintf(valChar, "%d", txbpf[0]);
+	Dlg->TXBPFWidthA->setText(valChar);
+	sprintf(valChar, "%d", txbpf[1]);
+	Dlg->TXBPFWidthB->setText(valChar);
+	sprintf(valChar, "%d", txbpf[2]);
+	Dlg->TXBPFWidthC->setText(valChar);
+	sprintf(valChar, "%d", txbpf[3]);
+	Dlg->TXBPFWidthD->setText(valChar);
+
+	sprintf(valChar, "%d", lpf[0]);
+	Dlg->LPFWidthA->setText(valChar);
+	sprintf(valChar, "%d", lpf[1]);
+	Dlg->LPFWidthB->setText(valChar);
+	sprintf(valChar, "%d", lpf[2]);
+	Dlg->LPFWidthC->setText(valChar);
+	sprintf(valChar, "%d", lpf[4]);
+	Dlg->LPFWidthD->setText(valChar);
+
+	sprintf(valChar, "%d", BPF_tap[0]);
+	Dlg->BPFTapsA->setText(valChar);
+	sprintf(valChar, "%d", BPF_tap[1]);
+	Dlg->BPFTapsB->setText(valChar);
+	sprintf(valChar, "%d", BPF_tap[2]);
+	Dlg->BPFTapsC->setText(valChar);
+	sprintf(valChar, "%d", BPF_tap[3]);
+	Dlg->BPFTapsD->setText(valChar);
+
+	sprintf(valChar, "%d", LPF_tap[0]);
+	Dlg->LPFTapsA->setText(valChar);
+	sprintf(valChar, "%d", LPF_tap[1]);
+	Dlg->LPFTapsB->setText(valChar);
+	sprintf(valChar, "%d", LPF_tap[2]);
+	Dlg->LPFTapsC->setText(valChar);
+	sprintf(valChar, "%d", LPF_tap[3]);
+	Dlg->LPFTapsD->setText(valChar);
+
+	Dlg->preEmphAllA->setChecked(emph_all[0]);
+
+	if (emph_all[0])
+		Dlg->preEmphA->setDisabled(TRUE);
+	else
+		Dlg->preEmphA->setCurrentIndex(emph_db[0]);
+
+	Dlg->preEmphAllB->setChecked(emph_all[1]);
+
+	if (emph_all[1])
+		Dlg->preEmphB->setDisabled(TRUE);
+	else
+		Dlg->preEmphB->setCurrentIndex(emph_db[1]);
+
+	Dlg->preEmphAllC->setChecked(emph_all[2]);
+
+	if (emph_all[2])
+		Dlg->preEmphC->setDisabled(TRUE);
+	else
+		Dlg->preEmphC->setCurrentIndex(emph_db[2]);
+
+	Dlg->preEmphAllD->setChecked(emph_all[3]);
+
+	if (emph_all[3])
+		Dlg->preEmphD->setDisabled(TRUE);
+	else
+		Dlg->preEmphD->setCurrentIndex(emph_db[3]);
+
+
+	Dlg->nonAX25A->setChecked(NonAX25[0]);
+	Dlg->nonAX25B->setChecked(NonAX25[1]);
+	Dlg->nonAX25C->setChecked(NonAX25[2]);
+	Dlg->nonAX25D->setChecked(NonAX25[3]);
+
+	Dlg->KISSOptA->setChecked(KISS_opt[0]);
+	Dlg->KISSOptB->setChecked(KISS_opt[1]);
+	Dlg->KISSOptC->setChecked(KISS_opt[2]);
+	Dlg->KISSOptD->setChecked(KISS_opt[3]);
+
+	sprintf(valChar, "%d", maxframe[0]);
+	Dlg->MaxFrameA->setText(valChar);
+	sprintf(valChar, "%d", maxframe[1]);
+	Dlg->MaxFrameB->setText(valChar);
+	sprintf(valChar, "%d", maxframe[2]);
+	Dlg->MaxFrameC->setText(valChar);
+	sprintf(valChar, "%d", maxframe[3]);
+	Dlg->MaxFrameD->setText(valChar);
+
+	sprintf(valChar, "%d", txdelay[0]);
+	Dlg->TXDelayA->setText(valChar);
+	sprintf(valChar, "%d", txdelay[1]);
+	Dlg->TXDelayB->setText(valChar);
+	sprintf(valChar, "%d", txdelay[2]);
+	Dlg->TXDelayC->setText(valChar);
+	sprintf(valChar, "%d", txdelay[3]);
+	Dlg->TXDelayD->setText(valChar);
+
+
+	sprintf(valChar, "%d", txtail[0]);
+	Dlg->TXTailA->setText(valChar);
+	sprintf(valChar, "%d", txtail[1]);
+	Dlg->TXTailB->setText(valChar);
+	sprintf(valChar, "%d", txtail[2]);
+	Dlg->TXTailC->setText(valChar);
+	sprintf(valChar, "%d", txtail[3]);
+	Dlg->TXTailD->setText(valChar);
+
+	Dlg->FrackA->setText(QString::number(frack_time[0]));
+	Dlg->FrackB->setText(QString::number(frack_time[1]));
+	Dlg->FrackC->setText(QString::number(frack_time[2]));
+	Dlg->FrackD->setText(QString::number(frack_time[3]));
+
+	Dlg->RetriesA->setText(QString::number(fracks[0]));
+	Dlg->RetriesB->setText(QString::number(fracks[1]));
+	Dlg->RetriesC->setText(QString::number(fracks[2]));
+	Dlg->RetriesD->setText(QString::number(fracks[3]));
+
+	sprintf(valChar, "%d", RCVR[0]);
+	Dlg->AddRXA->setText(valChar);
+	sprintf(valChar, "%d", RCVR[1]);
+	Dlg->AddRXB->setText(valChar);
+	sprintf(valChar, "%d", RCVR[2]);
+	Dlg->AddRXC->setText(valChar);
+	sprintf(valChar, "%d", RCVR[3]);
+	Dlg->AddRXD->setText(valChar);
+
+	sprintf(valChar, "%d", rcvr_offset[0]);
+	Dlg->RXShiftA->setText(valChar);
+
+	sprintf(valChar, "%d", rcvr_offset[1]);
+	Dlg->RXShiftB->setText(valChar);
+
+	sprintf(valChar, "%d", rcvr_offset[2]);
+	Dlg->RXShiftC->setText(valChar);
+	sprintf(valChar, "%d", rcvr_offset[3]);
+	Dlg->RXShiftD->setText(valChar);
+
+	//	speed[1]
+	//	speed[2];
+
+	Dlg->recoverBitA->setCurrentIndex(recovery[0]);
+	Dlg->recoverBitB->setCurrentIndex(recovery[1]);
+	Dlg->recoverBitC->setCurrentIndex(recovery[2]);
+	Dlg->recoverBitD->setCurrentIndex(recovery[3]);
+
+	Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]);
+	Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]);
+	Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]);
+	Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]);
+
+	Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]);
+	Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]);
+	Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]);
+	Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]);
+
+	Dlg->CRC_A->setChecked(il2p_crc[0]);
+	Dlg->CRC_B->setChecked(il2p_crc[1]);
+	Dlg->CRC_C->setChecked(il2p_crc[2]);
+	Dlg->CRC_D->setChecked(il2p_crc[3]);
+
+	Dlg->CWIDCall->setText(CWIDCall);
+	Dlg->CWIDInterval->setText(QString::number(CWIDInterval));
+	Dlg->CWIDMark->setText(CWIDMark);
+
+	if (CWIDType)
+		Dlg->radioButton_2->setChecked(1);
+	else
+		Dlg->CWIDType->setChecked(1);
+
+	Dlg->afterTraffic->setChecked(afterTraffic);
+
+	Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]);
+	Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]);
+	Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]);
+	Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]);
+
+	Dlg->RSIDUI_A->setChecked(RSID_UI[0]);
+	Dlg->RSIDUI_B->setChecked(RSID_UI[1]);
+	Dlg->RSIDUI_C->setChecked(RSID_UI[2]);
+	Dlg->RSIDUI_D->setChecked(RSID_UI[3]);
+
+	Dlg->DigiCallsA->setText(MyDigiCall[0]);
+	Dlg->DigiCallsB->setText(MyDigiCall[1]);
+	Dlg->DigiCallsC->setText(MyDigiCall[2]);
+	Dlg->DigiCallsD->setText(MyDigiCall[3]);
+
+	Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]);
+	Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]);
+	Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]);
+	Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]);
+	
+	connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+
+	connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+
+	connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+
+	connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+	connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+
+	connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept()));
+	connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave()));
+	connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject()));
+
+	connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA()));
+	connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB()));
+	connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC()));
+	connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD()));
+
+	connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int)));
+	connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int)));
+	connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int)));
+	connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int)));
+
+	UI.exec();
+}
+
+void QtSoundModem::preEmphAllAChanged(int state)
+{
+	Dlg->preEmphA->setDisabled(state);
+}
+
+void QtSoundModem::preEmphAllBChanged(int state)
+{
+	Dlg->preEmphB->setDisabled(state);
+}
+
+void QtSoundModem::preEmphAllCChanged(int state)
+{
+	Dlg->preEmphC->setDisabled(state);
+}
+
+void QtSoundModem::preEmphAllDChanged(int state)
+{
+	Dlg->preEmphD->setDisabled(state);
+}
+
+extern "C" void get_exclude_list(char * line, TStringList * list);
+
+void QtSoundModem::modemaccept()
+{
+	modemSave();
+
+	AGW_Report_Modem_Change(0);
+	AGW_Report_Modem_Change(1);
+	AGW_Report_Modem_Change(2);
+	AGW_Report_Modem_Change(3);
+
+	delete(Dlg);
+	saveSettings();
+
+	modemUI->accept();
+
+}
+
+void QtSoundModem::modemSave()
+{
+	QVariant Q;
+	
+	emph_all[0] = Dlg->preEmphAllA->isChecked();
+	emph_db[0] = Dlg->preEmphA->currentIndex();
+
+	emph_all[1] = Dlg->preEmphAllB->isChecked();
+	emph_db[1] = Dlg->preEmphB->currentIndex();
+
+	emph_all[2] = Dlg->preEmphAllC->isChecked();
+	emph_db[2] = Dlg->preEmphC->currentIndex();
+
+	emph_all[3] = Dlg->preEmphAllD->isChecked();
+	emph_db[3] = Dlg->preEmphD->currentIndex();
+
+	NonAX25[0] = Dlg->nonAX25A->isChecked();
+	NonAX25[1] = Dlg->nonAX25B->isChecked();
+	NonAX25[2] = Dlg->nonAX25C->isChecked();
+	NonAX25[3] = Dlg->nonAX25D->isChecked();
+
+	KISS_opt[0] = Dlg->KISSOptA->isChecked();
+	KISS_opt[1] = Dlg->KISSOptB->isChecked();
+	KISS_opt[2] = Dlg->KISSOptC->isChecked();
+	KISS_opt[3] = Dlg->KISSOptD->isChecked();
+
+	if (emph_db[0] < 0 || emph_db[0] > nr_emph)
+		emph_db[0] = 0;
+
+	if (emph_db[1] < 0 || emph_db[1] > nr_emph)
+		emph_db[1] = 0;
+
+	if (emph_db[2] < 0 || emph_db[2] > nr_emph)
+		emph_db[2] = 0;
+
+	if (emph_db[3] < 0 || emph_db[3] > nr_emph)
+		emph_db[3] = 0;
+
+	Q = Dlg->TXDelayA->text();
+	txdelay[0] = Q.toInt();
+
+	Q = Dlg->TXDelayB->text();
+	txdelay[1] = Q.toInt();
+	
+	Q = Dlg->TXDelayC->text();
+	txdelay[2] = Q.toInt();
+
+	Q = Dlg->TXDelayD->text();
+	txdelay[3] = Q.toInt();
+
+	Q = Dlg->MaxFrameA->text();
+	maxframe[0] = Q.toInt();
+
+	Q = Dlg->MaxFrameB->text();
+	maxframe[1] = Q.toInt();
+
+	Q = Dlg->MaxFrameC->text();
+	maxframe[2] = Q.toInt();
+
+	Q = Dlg->MaxFrameD->text();
+	maxframe[3] = Q.toInt();
+
+	if (maxframe[0] == 0 || maxframe[0] > 7) maxframe[0] = 3;
+	if (maxframe[1] == 0 || maxframe[1] > 7) maxframe[1] = 3;
+	if (maxframe[2] == 0 || maxframe[2] > 7) maxframe[2] = 3;
+	if (maxframe[3] == 0 || maxframe[3] > 7) maxframe[3] = 3;
+
+	Q = Dlg->TXTailA->text();
+	txtail[0] = Q.toInt();
+
+	Q = Dlg->TXTailB->text();
+	txtail[1] = Q.toInt();
+
+	Q = Dlg->TXTailC->text();
+	txtail[2] = Q.toInt();
+
+	txtail[3] = Dlg->TXTailD->text().toInt();
+
+	frack_time[0] = Dlg->FrackA->text().toInt();
+	frack_time[1] = Dlg->FrackB->text().toInt();
+	frack_time[2] = Dlg->FrackC->text().toInt();
+	frack_time[3] = Dlg->FrackD->text().toInt();
+
+	fracks[0] = Dlg->RetriesA->text().toInt();
+	fracks[1] = Dlg->RetriesB->text().toInt();
+	fracks[2] = Dlg->RetriesC->text().toInt();
+	fracks[3] = Dlg->RetriesD->text().toInt();
+
+	Q = Dlg->AddRXA->text();
+	RCVR[0] = Q.toInt();
+
+	Q = Dlg->AddRXB->text();
+	RCVR[1] = Q.toInt();
+
+	Q = Dlg->AddRXC->text();
+	RCVR[2] = Q.toInt();
+
+	Q = Dlg->AddRXD->text();
+	RCVR[3] = Q.toInt();
+
+	Q = Dlg->RXShiftA->text();
+	rcvr_offset[0] = Q.toInt();
+
+	Q = Dlg->RXShiftB->text();
+	rcvr_offset[1] = Q.toInt();
+
+	Q = Dlg->RXShiftC->text();
+	rcvr_offset[2] = Q.toInt();
+
+	Q = Dlg->RXShiftD->text();
+	rcvr_offset[3] = Q.toInt();
+
+	fx25_mode[0] = Dlg->fx25ModeA->currentIndex();
+	fx25_mode[1] = Dlg->fx25ModeB->currentIndex();
+	fx25_mode[2] = Dlg->fx25ModeC->currentIndex();
+	fx25_mode[3] = Dlg->fx25ModeD->currentIndex();
+
+	il2p_mode[0] = Dlg->IL2PModeA->currentIndex();
+	il2p_mode[1] = Dlg->IL2PModeB->currentIndex();
+	il2p_mode[2] = Dlg->IL2PModeC->currentIndex();
+	il2p_mode[3] = Dlg->IL2PModeD->currentIndex();
+
+	il2p_crc[0] = Dlg->CRC_A->isChecked();
+	il2p_crc[1] = Dlg->CRC_B->isChecked();
+	il2p_crc[2] = Dlg->CRC_C->isChecked();
+	il2p_crc[3] = Dlg->CRC_D->isChecked();
+
+	recovery[0] = Dlg->recoverBitA->currentIndex();
+	recovery[1] = Dlg->recoverBitB->currentIndex();
+	recovery[2] = Dlg->recoverBitC->currentIndex();
+	recovery[3] = Dlg->recoverBitD->currentIndex();
+
+
+	strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper());
+	strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper());
+	CWIDInterval = Dlg->CWIDInterval->text().toInt();
+	CWIDType = Dlg->radioButton_2->isChecked();
+
+	afterTraffic = Dlg->afterTraffic->isChecked();
+
+	if (CWIDInterval && afterTraffic == false)
+		cwidtimer->start(CWIDInterval * 60000);
+	else
+		cwidtimer->stop();
+
+
+	RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked();
+	RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked();
+	RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked();
+	RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked();
+
+	RSID_UI[0] = Dlg->RSIDUI_A->isChecked();
+	RSID_UI[1] = Dlg->RSIDUI_B->isChecked();
+	RSID_UI[2] = Dlg->RSIDUI_C->isChecked();
+	RSID_UI[3] = Dlg->RSIDUI_D->isChecked();
+
+	RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked();
+	RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked();
+	RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked();
+	RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked();
+
+	Q = Dlg->DigiCallsA->text();
+	strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper());
+
+	Q = Dlg->DigiCallsB->text();
+	strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper());
+
+	Q = Dlg->DigiCallsC->text();
+	strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper());
+
+	Q = Dlg->DigiCallsD->text();
+	strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper());
+
+	int i;
+
+	for (i = 0; i < 4; i++)
+	{
+		initTStringList(&list_digi_callsigns[i]);
+		get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]);
+	}
+
+/*
+	Q = Dlg->LPFWidthA->text();
+	lpf[0] = Q.toInt();
+
+	Q = Dlg->LPFWidthB->text();
+	lpf[1] = Q.toInt();
+
+	Q = Dlg->LPFWidthC->text();
+	lpf[2] = Q.toInt();
+
+	Q = Dlg->LPFWidthD->text();
+	lpf[3] = Q.toInt();
+*/
+
+}
+
+void QtSoundModem::modemreject()
+{
+	delete(Dlg);
+	modemUI->reject();
+}
+
+void QtSoundModem::doRSIDA()
+{
+	needRSID[0] = 1;
+}
+
+void QtSoundModem::doRSIDB()
+{
+	needRSID[1] = 1;
+}
+
+void QtSoundModem::doRSIDC()
+{
+	needRSID[2] = 1;
+}
+
+void QtSoundModem::doRSIDD()
+{
+	needRSID[3] = 1;
+}
+
+
+
+
+void QtSoundModem::doFilter(int Chan, int Filter)
+{
+	Ui_Dialog Dev;
+	QImage * bitmap;
+
+	QDialog UI;
+
+	Dev.setupUi(&UI);
+
+	bitmap = new QImage(642, 312, QImage::Format_RGB32);
+
+	bitmap->fill(qRgb(255, 255, 255));
+
+	QPainter qPainter(bitmap);
+	qPainter.setBrush(Qt::NoBrush);
+	qPainter.setPen(Qt::black);
+
+	if (Filter == 0)
+		make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter);
+	else if (Filter == 1)
+		make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter);
+	else
+		make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter);
+
+	qPainter.end();
+	Dev.label->setPixmap(QPixmap::fromImage(*bitmap));
+
+	UI.exec();
+
+}
+
+Ui_devicesDialog * Dev;
+
+char NewPTTPort[80];
+
+int newSoundMode = 0;
+int oldSoundMode = 0;
+
+void QtSoundModem::SoundModeChanged(bool State)
+{
+	UNUSED(State);
+
+	// Mustn't change SoundMode until dialog is accepted
+
+	if (Dev->UDP->isChecked())
+		newSoundMode = 3;
+	else if (Dev->PULSE->isChecked())
+		newSoundMode = 2;
+	else
+		newSoundMode = Dev->OSS->isChecked();
+
+}
+
+void QtSoundModem::DualPTTChanged(bool State)
+{
+	UNUSED(State);
+
+	// Forse Evaluation of Cat Port setting
+
+	PTTPortChanged(0);
+}
+
+void QtSoundModem::CATChanged(bool State)
+{
+	UNUSED(State);
+	PTTPortChanged(0);
+}
+
+void QtSoundModem::PTTPortChanged(int Selected)
+{
+	UNUSED(Selected);
+
+	QVariant Q = Dev->PTTPort->currentText();
+	strcpy(NewPTTPort, Q.toString().toUtf8());
+
+	Dev->RTSDTR->setVisible(false);
+	Dev->CAT->setVisible(false);
+
+	Dev->PTTOnLab->setVisible(false);
+	Dev->PTTOn->setVisible(false);
+	Dev->PTTOff->setVisible(false);
+	Dev->PTTOffLab->setVisible(false);
+	Dev->CATLabel->setVisible(false);
+	Dev->CATSpeed->setVisible(false);
+
+	Dev->GPIOLab->setVisible(false);
+	Dev->GPIOLeft->setVisible(false);
+	Dev->GPIORight->setVisible(false);
+	Dev->GPIOLab2->setVisible(false);
+
+	Dev->CM108Label->setVisible(false);
+	Dev->VIDPID->setVisible(false);
+
+	if (strcmp(NewPTTPort, "None") == 0)
+	{
+	}
+	else if (strcmp(NewPTTPort, "GPIO") == 0)
+	{
+		Dev->GPIOLab->setVisible(true);
+		Dev->GPIOLeft->setVisible(true);
+		if (Dev->DualPTT->isChecked())
+		{
+			Dev->GPIORight->setVisible(true);
+			Dev->GPIOLab2->setVisible(true);
+		}
+	}
+
+	else if (strcmp(NewPTTPort, "CM108") == 0)
+	{
+		Dev->CM108Label->setVisible(true);
+//#ifdef __ARM_ARCHX
+		Dev->CM108Label->setText("CM108 Device");
+//#else
+//		Dev->CM108Label->setText("CM108 VID/PID");
+//#endif
+		Dev->VIDPID->setText(CM108Addr);
+		Dev->VIDPID->setVisible(true);
+	}
+	else if (strcmp(NewPTTPort, "HAMLIB") == 0)
+	{
+		Dev->CM108Label->setVisible(true);
+		Dev->CM108Label->setText("rigctrld Port");
+		Dev->VIDPID->setText(QString::number(HamLibPort));
+		Dev->VIDPID->setVisible(true);
+		Dev->PTTOnLab->setText("rigctrld Host");
+		Dev->PTTOnLab->setVisible(true);
+		Dev->PTTOn->setText(HamLibHost);
+		Dev->PTTOn->setVisible(true);
+	}
+	else
+	{
+		Dev->RTSDTR->setVisible(true);
+		Dev->CAT->setVisible(true);
+
+		if (Dev->CAT->isChecked())
+		{
+			Dev->PTTOnLab->setVisible(true);
+			Dev->PTTOnLab->setText("PTT On String");
+			Dev->PTTOn->setText(PTTOnString);
+			Dev->PTTOn->setVisible(true);
+			Dev->PTTOff->setVisible(true);
+			Dev->PTTOff->setText(PTTOffString);
+			Dev->PTTOffLab->setVisible(true);
+			Dev->CATLabel->setVisible(true);
+			Dev->CATSpeed->setVisible(true);
+		}
+	}
+}
+
+bool myResize::eventFilter(QObject *obj, QEvent *event)
+{
+	if (event->type() == QEvent::Resize)
+	{
+		QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
+		QSize size = resizeEvent->size();
+		int h = size.height();
+		int w = size.width();
+
+		if (obj == deviceUI)
+			Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));
+		else
+			Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));
+
+		return true;
+	}
+	return QObject::eventFilter(obj, event);
+}
+
+void QtSoundModem::doDevices()
+{
+	char valChar[10];
+
+	Dev = new(Ui_devicesDialog);
+
+	QDialog UI;
+
+	int i;
+
+	Dev->setupUi(&UI);
+
+	deviceUI = &UI;
+	modemUI = 0;
+
+	myResize *resize = new myResize();
+
+	UI.installEventFilter(resize);
+
+	newSoundMode = SoundMode;
+	oldSoundMode = SoundMode;
+
+#ifdef WIN32
+	Dev->ALSA->setText("WaveOut");
+	Dev->OSS->setVisible(0);
+	Dev->PULSE->setVisible(0);
+#endif
+
+	if (SoundMode == 0)
+		Dev->ALSA->setChecked(1);
+	else if (SoundMode == 1)
+		Dev->OSS->setChecked(1);
+	else if (SoundMode == 2)
+		Dev->PULSE->setChecked(1);
+	else if (SoundMode == 2)
+		Dev->UDP->setChecked(1);
+
+	connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
+	connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
+	connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
+	connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
+
+	for (i = 0; i < PlaybackCount; i++)
+		Dev->outputDevice->addItem(&PlaybackNames[i][0]);
+
+	i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);
+
+
+	if (i == -1)
+	{
+		// Add device to list
+
+		Dev->outputDevice->addItem(PlaybackDevice);
+		i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);
+	}
+
+	Dev->outputDevice->setCurrentIndex(i);
+
+	for (i = 0; i < CaptureCount; i++)
+		Dev->inputDevice->addItem(&CaptureNames[i][0]);
+
+	i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);
+
+	if (i == -1)
+	{
+		// Add device to list
+
+		Dev->inputDevice->addItem(CaptureDevice);
+		i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);
+	}
+	Dev->inputDevice->setCurrentIndex(i);
+
+	Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]);
+	Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]);
+	Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]);
+	Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]);
+
+	// Disable "None" option in first modem
+
+	QStandardItemModel *model = dynamic_cast<QStandardItemModel *>(Dev->Modem_1_Chan->model());
+	QStandardItem * item = model->item(0, 0);
+	item->setEnabled(false);
+
+	Dev->singleChannelOutput->setChecked(SCO);
+	Dev->colourWaterfall->setChecked(raduga);
+
+	sprintf(valChar, "%d", KISSPort);
+	Dev->KISSPort->setText(valChar);
+	Dev->KISSEnabled->setChecked(KISSServ);
+
+	sprintf(valChar, "%d", AGWPort);
+	Dev->AGWPort->setText(valChar);
+	Dev->AGWEnabled->setChecked(AGWServ);
+
+	Dev->PTTOn->setText(PTTOnString);
+	Dev->PTTOff->setText(PTTOffString);
+
+	sprintf(valChar, "%d", PTTBAUD);
+	Dev->CATSpeed->setText(valChar);
+
+	sprintf(valChar, "%d", UDPClientPort);
+	Dev->UDPPort->setText(valChar);
+	Dev->UDPTXHost->setText(UDPHost);
+
+	if (UDPServerPort != TXPort)
+		sprintf(valChar, "%d/%d", UDPServerPort, TXPort);
+	else
+		sprintf(valChar, "%d", UDPServerPort);
+
+	Dev->UDPTXPort->setText(valChar);
+
+	Dev->UDPEnabled->setChecked(UDPServ);
+
+	sprintf(valChar, "%d", pttGPIOPin);
+	Dev->GPIOLeft->setText(valChar);
+	sprintf(valChar, "%d", pttGPIOPinR);
+	Dev->GPIORight->setText(valChar);
+
+	Dev->VIDPID->setText(CM108Addr);
+
+	QStringList items;
+
+	connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool)));
+	connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool)));
+	connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));
+
+	if (PTTMode == PTTCAT)
+		Dev->CAT->setChecked(true);
+	else
+		Dev->RTSDTR->setChecked(true);
+
+	for (const QSerialPortInfo &info : Ports)
+	{
+		items.append(info.portName());
+	}
+
+	items.sort();
+
+	Dev->PTTPort->addItem("None");
+	Dev->PTTPort->addItem("CM108");
+
+	//#ifdef __ARM_ARCH
+
+	Dev->PTTPort->addItem("GPIO");
+
+	//#endif
+
+	Dev->PTTPort->addItem("HAMLIB");
+
+	for (const QString &info : items)
+	{
+		Dev->PTTPort->addItem(info);
+	}
+
+	// If we are using a user specifed device add it
+
+	i = Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString);
+
+	if (i == -1)
+	{
+		// Add our device to list
+
+		Dev->PTTPort->insertItem(0, PTTPort);
+		i = Dev->PTTPort->findText(PTTPort, Qt::MatchContains);
+	}
+
+	Dev->PTTPort->setCurrentIndex(i);
+
+	PTTPortChanged(0);				// Force reevaluation
+
+	Dev->txRotation->setChecked(TX_rotate);
+	Dev->DualPTT->setChecked(DualPTT);
+
+	Dev->multiCore->setChecked(multiCore);
+	Dev->darkTheme->setChecked(darkTheme);
+
+	Dev->WaterfallMin->setCurrentIndex(Dev->WaterfallMin->findText(QString::number(WaterfallMin), Qt::MatchFixedString));
+	Dev->WaterfallMax->setCurrentIndex(Dev->WaterfallMax->findText(QString::number(WaterfallMax), Qt::MatchFixedString));
+
+	QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept()));
+	QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject()));
+
+	UI.exec();
+
+}
+
+void QtSoundModem::mysetstyle()
+{
+	if (darkTheme)
+	{
+		qApp->setStyleSheet(
+			"QWidget {color: white; background-color: black}"
+			"QTabBar::tab {color: rgb(127, 127, 127); background-color: black}"
+			"QTabBar::tab::selected {color: white}"
+			"QPushButton {border-style: outset; border-width: 2px; border-color: rgb(127, 127, 127)}"
+			"QPushButton::default {border-style: outset; border-width: 2px; border-color: white}");
+
+		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(40, 40, 40) }");
+
+		txText = qRgb(255, 127, 127);
+		rxText = qRgb(173, 216, 230);
+	}
+	else
+	{
+		qApp->setStyleSheet("");
+
+		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }");
+
+		txText = qRgb(192, 0, 0);
+		rxText = qRgb(0, 0, 192);
+	}
+}
+
+void QtSoundModem::deviceaccept()
+{
+	QVariant Q = Dev->inputDevice->currentText();
+	int cardChanged = 0;
+	char portString[32];
+	int newMax;
+	int newMin;
+
+	if (Dev->UDP->isChecked())
+	{
+		// cant have server and slave
+
+		if (Dev->UDPEnabled->isChecked())
+		{
+			QMessageBox::about(this, tr("QtSoundModem"),
+				tr("Can't have UDP sound source and UDP server at same time"));
+			return;
+		}
+	}
+
+	if (oldSoundMode != newSoundMode)
+	{
+		QMessageBox msgBox;
+
+		msgBox.setText("QtSoundModem must restart to change Sound Mode.\n"
+			"Program will close if you hit Ok\n"
+			"You will need to reselect audio devices after restarting");
+
+		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+
+		int i = msgBox.exec();
+
+		if (i == QMessageBox::Ok)
+		{			
+			SoundMode = newSoundMode;
+			saveSettings();
+
+			Closing = 1;
+			return;
+		}
+
+		if (oldSoundMode == 0)
+			Dev->ALSA->setChecked(1);
+		else if (oldSoundMode == 1)
+			Dev->OSS->setChecked(1);
+		else if (oldSoundMode == 2)
+			Dev->PULSE->setChecked(1);
+		else if (oldSoundMode == 3)
+			Dev->UDP->setChecked(1);
+
+		QMessageBox::about(this, tr("Info"),
+			tr("<p align = 'center'>Changes not saved</p>"));
+
+		return;
+
+	}
+
+	if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0)
+	{
+		strcpy(CaptureDevice, Q.toString().toUtf8());
+		cardChanged = 1;
+	}
+
+	CaptureIndex = Dev->inputDevice->currentIndex();
+
+	Q = Dev->outputDevice->currentText();
+
+	if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0)
+	{
+		strcpy(PlaybackDevice, Q.toString().toUtf8());
+		cardChanged = 1;
+	}
+
+	PlayBackIndex = Dev->outputDevice->currentIndex();
+
+	soundChannel[0] = Dev->Modem_1_Chan->currentIndex();
+	soundChannel[1] = Dev->Modem_2_Chan->currentIndex();
+	soundChannel[2] = Dev->Modem_3_Chan->currentIndex();
+	soundChannel[3] = Dev->Modem_4_Chan->currentIndex();
+
+	UsingLeft = 0;
+	UsingRight = 0;
+	UsingBothChannels = 0;
+
+	for (int i = 0; i < 4; i++)
+	{
+		if (soundChannel[i] == LEFT)
+		{
+			UsingLeft = 1;
+			modemtoSoundLR[i] = 0;
+		}
+		else if (soundChannel[i] == RIGHT)
+		{
+			UsingRight = 1;
+			modemtoSoundLR[i] = 1;
+		}
+	}
+
+	if (UsingLeft && UsingRight)
+		UsingBothChannels = 1;
+
+	SCO = Dev->singleChannelOutput->isChecked();
+	raduga = Dev->colourWaterfall->isChecked();
+	AGWServ = Dev->AGWEnabled->isChecked();
+	KISSServ = Dev->KISSEnabled->isChecked();
+
+	Q = Dev->KISSPort->text();
+	KISSPort = Q.toInt();
+
+	Q = Dev->AGWPort->text();
+	AGWPort = Q.toInt();
+
+	Q = Dev->PTTPort->currentText();
+	
+	char temp[256];
+
+	strcpy(temp, Q.toString().toUtf8());
+
+	if (strlen(temp))
+		strcpy(PTTPort, temp);
+
+	DualPTT = Dev->DualPTT->isChecked();
+	TX_rotate = Dev->txRotation->isChecked();
+	multiCore = Dev->multiCore->isChecked();
+	darkTheme = Dev->darkTheme->isChecked();
+	mysetstyle();
+
+	if (Dev->CAT->isChecked())
+		PTTMode = PTTCAT;
+	else
+		PTTMode = PTTRTS;
+
+	Q = Dev->PTTOn->text();
+	strcpy(PTTOnString, Q.toString().toUtf8());
+	Q = Dev->PTTOff->text();
+	strcpy(PTTOffString, Q.toString().toUtf8());
+
+	Q = Dev->CATSpeed->text();
+	PTTBAUD = Q.toInt();
+
+	Q = Dev->UDPPort->text();
+	UDPClientPort = Q.toInt();
+
+
+	Q = Dev->UDPTXPort->text();
+	strcpy(portString, Q.toString().toUtf8());
+	UDPServerPort = atoi(portString);
+
+	if (strchr(portString, '/'))
+	{
+		char * ptr = strlop(portString, '/');
+		TXPort = atoi(ptr);
+	}
+	else
+		TXPort = UDPServerPort;
+
+	Q = Dev->UDPTXHost->text();
+	strcpy(UDPHost, Q.toString().toUtf8());
+
+	UDPServ = Dev->UDPEnabled->isChecked();
+
+	Q = Dev->GPIOLeft->text();
+	pttGPIOPin = Q.toInt();
+
+	Q = Dev->GPIORight->text();
+	pttGPIOPinR = Q.toInt();
+
+	Q = Dev->VIDPID->text();
+
+	if (strcmp(PTTPort, "CM108") == 0)
+		strcpy(CM108Addr, Q.toString().toUtf8());
+	else if (strcmp(PTTPort, "HAMLIB") == 0)
+	{
+		HamLibPort = Q.toInt();
+		Q = Dev->PTTOn->text();
+		strcpy(HamLibHost, Q.toString().toUtf8());
+	}
+
+	Q = Dev->WaterfallMax->currentText();
+	newMax = Q.toInt();
+
+	Q = Dev->WaterfallMin->currentText();
+	newMin = Q.toInt();
+
+	if (newMax != WaterfallMax || newMin != WaterfallMin)
+	{
+		QMessageBox msgBox;
+
+		msgBox.setText("QtSoundModem must restart to change Waterfall range. Program will close if you hit Ok\n"
+		"It may take up to 30 seconds for the program to start for the first time after changing settings");
+
+		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+
+		int i = msgBox.exec();
+
+		if (i == QMessageBox::Ok)
+		{
+			Configuring = 1;			// Stop Waterfall
+
+			WaterfallMax = newMax;
+			WaterfallMin = newMin;
+			saveSettings();
+			Closing = 1;
+			return;
+		}
+	}
+
+
+	ClosePTTPort();
+	OpenPTTPort();
+
+	NeedWaterfallHeaders = true;
+
+	delete(Dev);
+	saveSettings();
+	deviceUI->accept();
+
+	if (cardChanged)
+	{
+		InitSound(1);
+	}
+
+	// Reset title and tooltip in case ports changed 
+
+	char Title[128];
+	sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : "");
+	w->setWindowTitle(Title);
+
+	sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort);
+	if (trayIcon)
+		trayIcon->setToolTip(Title);
+
+	QSize newSize(this->size());
+	QSize oldSize(this->size());
+
+	QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize);
+
+	QCoreApplication::postEvent(this, myResizeEvent);  
+}
+
+void QtSoundModem::devicereject()
+{	
+	delete(Dev);
+	deviceUI->reject();
+}
+
+void QtSoundModem::handleButton(int Port, int Type)
+{
+	// interlock calib with CWID
+
+	if (calib_mode[0] == 4)		// CWID
+		return;
+	
+	doCalib(Port, Type);
+}
+
+
+
+void QtSoundModem::doRestartWF()
+{
+	if (inWaterfall)
+	{
+		// in waterfall update thread
+
+		wftimer->start(5000);
+		return;
+	}
+		
+	lockWaterfall = true;
+
+	if (Firstwaterfall | Secondwaterfall)
+	{
+		initWaterfall(0);
+		initWaterfall(1);
+	}
+
+	delete(RXLevel);
+	delete(ui.RXLevel);
+	ui.RXLevel = new QLabel(ui.centralWidget);
+	RXLevelCopy = ui.RXLevel;
+	ui.RXLevel->setGeometry(QRect(780, 14, 150, 11));
+	ui.RXLevel->setFrameShape(QFrame::Box);
+	ui.RXLevel->setFrameShadow(QFrame::Sunken);
+
+	delete(RXLevel2);
+	delete(ui.RXLevel2);
+
+	ui.RXLevel2 = new QLabel(ui.centralWidget);
+	RXLevel2Copy = ui.RXLevel2;
+
+	ui.RXLevel2->setGeometry(QRect(780, 23, 150, 11));
+	ui.RXLevel2->setFrameShape(QFrame::Box);
+	ui.RXLevel2->setFrameShadow(QFrame::Sunken);
+
+	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
+	RXLevel->fill(cyan);
+
+	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
+	RXLevel2->fill(white);
+
+	ui.RXLevel->setVisible(1);
+	ui.RXLevel2->setVisible(1);
+
+	lockWaterfall = false;
+}
+
+
+void QtSoundModem::doAbout()
+{
+	QMessageBox::about(this, tr("About"),
+		tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO"));
+}
+
+void QtSoundModem::doCalibrate()
+{
+	Ui_calDialog Calibrate;
+	{
+		QDialog UI;
+		Calibrate.setupUi(&UI);
+
+		connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+		connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot()));
+
+		/*
+		
+		connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); });
+		connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); });
+		connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); });
+		connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); });
+		connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); });
+		connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); });
+		connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); });
+		connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); });
+
+//		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2)));
+*/
+		UI.exec();
+	}
+}
+
+void QtSoundModem::RefreshSpectrum(unsigned char * Data)
+{
+	int i;
+
+	// Last 4 bytes are level busy and Tuning lines
+
+	Waterfall->fill(Black);
+
+	if (Data[206] != LastLevel)
+	{
+		LastLevel = Data[206];
+//		RefreshLevel(LastLevel);
+	}
+
+	if (Data[207] != LastBusy)
+	{
+		LastBusy = Data[207];
+//		Busy->setVisible(LastBusy);
+	}
+
+	for (i = 0; i < 205; i++)
+	{
+		int val = Data[0];
+
+		if (val > 63)
+			val = 63;
+
+		Waterfall->setPixel(i, val, Yellow);
+		if (val < 62)
+			Waterfall->setPixel(i, val + 1, Gold);
+		Data++;
+	}
+
+	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));
+
+}
+
+void RefreshLevel(unsigned int Level, unsigned int LevelR)
+{
+	// Redraw the RX Level Bar Graph
+
+	unsigned int  x, y;
+
+	for (x = 0; x < 150; x++)
+	{
+		for (y = 0; y < 10; y++)
+		{
+			if (x < Level)
+			{
+				if (Level < 50)
+					RXLevel->setPixel(x, y, yellow);
+				else if (Level > 135)
+					RXLevel->setPixel(x, y, red);
+				else
+					RXLevel->setPixel(x, y, green);
+			}
+			else
+				RXLevel->setPixel(x, y, white);
+		}
+	}
+	RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel));
+
+	for (x = 0; x < 150; x++)
+	{
+		for (y = 0; y < 10; y++)
+		{
+			if (x < LevelR)
+			{
+				if (LevelR < 50)
+					RXLevel2->setPixel(x, y, yellow);
+				else if (LevelR > 135)
+					RXLevel2->setPixel(x, y, red);
+				else
+					RXLevel2->setPixel(x, y, green);
+			}
+			else
+				RXLevel2->setPixel(x, y, white);
+		}
+	}
+	RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2));
+}
+
+extern "C" unsigned char CurrentLevel;
+extern "C" unsigned char CurrentLevelR;
+
+void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data)
+{
+	int j;
+	unsigned char * Line;
+	int len = Waterfall->bytesPerLine();
+	int TopLine = NextWaterfallLine[snd_ch];
+
+	// Write line to cyclic buffer then draw starting with the line just written
+
+	// Length is 208 bytes, including Level and Busy flags
+
+	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206);
+	if (NextWaterfallLine[snd_ch] > 63)
+		NextWaterfallLine[snd_ch] = 0;
+
+	for (j = 63; j > 0; j--)
+	{
+		Line = Waterfall->scanLine(j);
+		memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len);
+		if (TopLine > 63)
+			TopLine = 0;
+	}
+
+	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));
+}
+
+void QtSoundModem::sendtoTrace(char * Msg, int tx)
+{
+	const QTextCursor old_cursor = monWindowCopy->textCursor();
+	const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value();
+	const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum();
+
+	// Move the cursor to the end of the document.
+	monWindowCopy->moveCursor(QTextCursor::End);
+
+	// Insert the text at the position of the cursor (which is the end of the document).
+
+	if (tx)
+		monWindowCopy->setTextColor(txText);
+	else
+		monWindowCopy->setTextColor(rxText);
+
+	monWindowCopy->textCursor().insertText(Msg);
+
+	if (old_cursor.hasSelection() || !is_scrolled_down)
+	{
+		// The user has selected text or scrolled away from the bottom: maintain position.
+		monWindowCopy->setTextCursor(old_cursor);
+		monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value);
+	}
+	else
+	{
+		// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
+		monWindowCopy->moveCursor(QTextCursor::End);
+		monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum());
+	}
+
+	free(Msg);
+}
+
+
+// I think this does the waterfall
+
+typedef struct TRGBQ_t
+{
+	Byte b, g, r, re;
+
+} TRGBWQ;
+
+typedef struct tagRECT
+{
+	int    left;
+	int    top;
+	int    right;
+	int    bottom;
+} RECT;
+
+unsigned int RGBWF[256] ;
+
+
+extern "C" void init_raduga()
+{
+	Byte offset[6] = {0, 51, 102, 153, 204};
+	Byte i, n;
+
+	for (n = 0; n < 52; n++)
+	{
+		i = n * 5;
+
+		RGBWF[n + offset[0]] = qRgb(0, 0, i);
+		RGBWF[n + offset[1]] = qRgb(0, i, 255);
+		RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i);
+		RGBWF[n + offset[3]] = qRgb(1, 255, 0);
+		RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0);
+	}
+}
+
+extern "C" int nonGUIMode;
+
+
+// This draws the Frequency Scale on Waterfall
+
+extern "C" void DrawFreqTicks()
+{
+	if (nonGUIMode)
+		return;
+
+	// Draw Frequency Markers on waterfall header(s);
+
+	int x, i;
+	char Textxx[20];
+	QImage * bm = Waterfall;
+
+	QPainter qPainter(bm);
+	qPainter.setBrush(Qt::black);
+	qPainter.setPen(Qt::white);
+
+	int Chan;
+
+#ifdef WIN32
+		int Top = 3;
+#else
+		int Top = 4;
+#endif
+		int Base = 0;
+
+		for (Chan = 0; Chan < 2; Chan++)
+		{
+			if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
+				sprintf(Textxx, "Right");
+			else
+				sprintf(Textxx, "Left");
+
+			qPainter.drawText(2, Top, 100, 20, 0, Textxx);
+
+			// We drew markers every 100 Hz or 100 / binsize pixels
+
+			int Markers = ((WaterfallMax - WaterfallMin) / 100) + 5;			// Number of Markers to draw
+			int Freq = WaterfallMin;
+			float PixelsPerMarker = 100.0 / BinSize;
+
+			for (i = 0; i < Markers; i++)
+			{
+				x = round(PixelsPerMarker * i);
+				if (x < 1025)
+				{
+					if ((Freq % 500) == 0)
+						qPainter.drawLine(x, Base + 22, x, Base + 15);
+					else
+						qPainter.drawLine(x, Base + 22, x, Base + 18);
+
+					if ((Freq % 500) == 0)
+					{
+						sprintf(Textxx, "%d", Freq);
+
+						if (x < 924)
+							qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx);
+					}
+				}
+				Freq += 100;
+			}
+
+			if (UsingBothChannels == 0)
+				break;
+
+			Top += WaterfallTotalPixels;
+			Base = WaterfallTotalPixels;
+		}
+
+}
+
+// These draws the frequency Markers on the Waterfall
+
+void DrawModemFreqRange()
+{
+	if (nonGUIMode)
+		return;
+
+	// Draws the modem freq bars on waterfall header(s)
+
+
+	int x1, x2, k, pos1, pos2, pos3;
+	QImage * bm = Waterfall;
+
+	QPainter qPainter(bm);
+	qPainter.setBrush(Qt::NoBrush);
+	qPainter.setPen(Qt::white);
+
+	int Chan;
+	int LRtoDisplay = LEFT;
+	int top = 0;
+
+	for (Chan = 0; Chan < 2; Chan++)
+	{
+		if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
+			LRtoDisplay = RIGHT;
+
+		//	bm->fill(black);
+
+	//	qPainter.fillRect(top, 23, 1024, 10, Qt::black);
+
+		// We drew markers every 100 Hz or 100 / binsize pixels
+
+		float PixelsPerHz = 1.0 / BinSize;
+		k = 26 + top;
+
+		// draw all enabled ports on the ports on this soundcard
+
+		// First Modem is always on the first waterfall
+		// If second is enabled it is on the first unless different
+		//		channel from first
+
+		for (int i = 0; i < 4; i++)
+		{
+			if (soundChannel[i] != LRtoDisplay)
+					continue;
+
+			pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
+			pos2 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
+			pos3 = roundf(((rxOffset + chanOffset[i] + rx_freq[i])) - WaterfallMin * PixelsPerHz);
+			x1 = pos1 + 5;
+			x2 = pos2 + 5;
+
+			qPainter.setPen(Qt::white);
+			qPainter.drawLine(x1, k, x2, k);
+			qPainter.drawLine(x1, k - 3, x1, k + 3);
+			qPainter.drawLine(x2, k - 3, x2, k + 3);
+			//		qPainter.drawLine(pos3, k - 3, pos3, k + 3);
+
+			if (rxOffset || chanOffset[i])
+			{
+				// Draw TX posn if rxOffset used
+
+				pos3 = roundf((rx_freq[i] - WaterfallMin) * PixelsPerHz);
+				qPainter.setPen(Qt::magenta);
+				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
+				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
+				qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3);
+			}
+
+			k += 3;
+		}
+		if (UsingBothChannels == 0)
+			break;
+
+		LRtoDisplay = RIGHT;
+		top = WaterfallTotalPixels;
+	}
+}
+
+
+void doWaterfallThread(void * param);
+
+extern "C" void doWaterfall(int snd_ch)
+{
+	if (nonGUIMode)
+		return;
+
+	if (Closing)
+		return;
+
+	if (lockWaterfall)
+		return;
+
+//	if (multiCore)			// Run modems in separate threads
+//		_beginthread(doWaterfallThread, 0, xx);
+//	else
+		doWaterfallThread((void *)(size_t)snd_ch);
+
+}
+
+extern "C" void displayWaterfall()
+{
+	// if we are using both channels but only want right need to extract correct half of Image
+
+	if (Waterfall == nullptr)
+		return;
+
+	if (UsingBothChannels && (Firstwaterfall == 0))
+		WaterfallCopy->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
+	else
+		WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+
+	WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall));
+}
+
+extern "C" float aFFTAmpl[1024];
+extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag);
+
+void doWaterfallThread(void * param)
+{
+	int snd_ch = (int)(size_t)param;
+
+	if (lockWaterfall)
+		return;
+
+	if (Configuring)
+		return;
+
+	inWaterfall = true;					// don't allow restart waterfall
+
+	if (snd_ch == 1 && UsingLeft == 0)	// Only using right
+		snd_ch = 0;						// Samples are in first buffer
+
+	QImage * bm = Waterfall;
+
+	int  i;
+	single  mag;
+	UCHAR * p;
+	UCHAR Line[4096] = "";			// 4 bytes per pixel
+
+	int lineLen, Start, End;
+	word  hFFTSize;
+	Byte  n;
+	float RealOut[8192] = { 0 };
+	float ImagOut[8192];
+
+
+	RefreshLevel(CurrentLevel, CurrentLevelR);	// Signal Level
+
+	hFFTSize = FFTSize / 2;
+
+
+	// I think an FFT should produce n/2 bins, each of Samp/n Hz
+	// Looks like my code only works with n a power of 2
+
+	// So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel 
+	// display (is this enough?)
+
+	Start = (WaterfallMin / BinSize);		// First and last bins to process
+	End = (WaterfallMax / BinSize);
+
+	if (0)	//RSID_WF
+	{
+		// Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE];
+
+		for (i = 0; i < hFFTSize; i++)
+		{
+			mag = aFFTAmpl[i];
+
+			mag *= 0.00000042f;
+
+			if (mag < 0.00001f)
+				mag = 0.00001f;
+
+			if (mag > 1.0f)
+				mag = 1.0f;
+
+			mag = 22 * log2f(mag) + 255;
+
+			if (mag < 0)
+				mag = 0;
+
+			fft_disp[snd_ch][i] = round(mag);
+		}
+	}
+	else
+	{
+		dofft(&fft_buf[snd_ch][0], RealOut, ImagOut);
+
+		//	FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0);
+
+		for (i = Start; i < End; i++)
+		{
+			//mag: = ComplexMag(fft_d[i])*0.00000042;
+
+	//		mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f;
+
+			mag = powf(RealOut[i], 2);
+			mag += powf(ImagOut[i], 2);
+			mag = sqrtf(mag);
+			mag *= 0.00000042f;
+
+
+			if (mag > MaxMagOut)
+			{
+				MaxMagOut = mag;
+				MaxMagIndex = i;
+			}
+
+			if (mag < 0.00001f)
+				mag = 0.00001f;
+
+			if (mag > 1.0f)
+				mag = 1.0f;
+
+			mag = 22 * log2f(mag) + 255;
+
+			if (mag < 0)
+				mag = 0;
+
+			MagOut[i] = mag;					// for Freq Guess
+			fft_disp[snd_ch][i] = round(mag);
+		}
+	}
+
+	SMUpdateBusyDetector(snd_ch, RealOut, ImagOut);
+
+
+
+	// we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display
+
+	if (bm == 0)
+	{
+		inWaterfall = false;
+		return;
+	}
+	if ((Firstwaterfall == 0 && snd_ch == 0) || (Secondwaterfall == 0 && snd_ch == 1))
+	{
+		inWaterfall = false;
+		return;
+	}
+
+	p = Line;
+	lineLen = 4096;
+
+	if (raduga == DISP_MONO)
+	{
+		for (i = Start; i < End; i++)
+		{
+			n = fft_disp[snd_ch][i];
+			*(p++) = n;					// all colours the same
+			*(p++) = n;
+			*(p++) = n;
+			p++;
+		}
+	}
+	else
+	{
+		for (i = Start; i < End; i++)
+		{
+			n = fft_disp[snd_ch][i];
+			memcpy(p, &RGBWF[n], 4);
+			p += 4;
+		}
+	}
+
+	// Scroll
+
+
+
+	int TopLine = NextWaterfallLine[snd_ch];
+	int TopScanLine = WaterfallHeaderPixels;
+
+	if (snd_ch)
+		TopScanLine += WaterfallTotalPixels;
+
+	// Write line to cyclic buffer then draw starting with the line just written
+
+	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096);
+	if (NextWaterfallLine[snd_ch] > 79)
+		NextWaterfallLine[snd_ch] = 0;
+	
+	// Sanity check
+
+	int WFMaxLine = bm->height();
+
+
+	if ((79 + TopScanLine) >= bm->height())
+	{
+		printf("Invalid WFMaxLine %d \n", bm->height());
+			exit(1);
+	}
+
+
+	for (int j = 79; j > 0; j--)
+	{
+		p = bm->scanLine(j + TopScanLine);
+		if (p == nullptr)
+		{
+			printf("Invalid WF Pointer \n");
+			exit(1);
+		}
+		memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen);
+		TopLine++;
+		if (TopLine > 79)
+			TopLine = 0;
+	}
+
+	inWaterfall = false;
+}
+
+
+void QtSoundModem::changeEvent(QEvent* e)
+{
+	if (e->type() == QEvent::WindowStateChange)
+	{
+		QWindowStateChangeEvent* ev = static_cast<QWindowStateChangeEvent*>(e);
+
+		qDebug() << windowState();
+
+		if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized)
+		{
+			if (trayIcon)
+				setVisible(false);
+		}
+//		if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState)
+//		{
+//			QMessageBox::information(this, "", "Window has been restored");
+//		}
+
+	}
+	QWidget::changeEvent(e);
+}
+
+#include <QCloseEvent>
+
+void QtSoundModem::closeEvent(QCloseEvent *event)
+{
+	UNUSED(event);
+
+	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
+	mysettings.setValue("geometry", QWidget::saveGeometry());
+	mysettings.setValue("windowState", saveState());
+
+	Closing = TRUE;
+	qDebug() << "Closing";
+
+	QThread::msleep(100);
+}
+
+	
+QtSoundModem::~QtSoundModem()
+{
+	qDebug() << "Saving Settings";
+		
+	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
+	mysettings.setValue("geometry", saveGeometry());
+	mysettings.setValue("windowState", saveState());
+	
+	saveSettings();	
+	Closing = TRUE;
+	qDebug() << "Closing";
+
+	QThread::msleep(100);
+}
+
+extern "C" void QSleep(int ms)
+{
+	QThread::msleep(ms);
+}
+
+int upd_time = 30;
+
+void QtSoundModem::show_grid()
+{
+	// This refeshes the session list
+
+	int  snd_ch, i, num_rows, row_idx;
+	QTableWidgetItem *item;
+	const char * msg;
+
+	int  speed_tx, speed_rx;
+
+	if (grid_time < 10)
+	{
+		grid_time++;
+		return;
+	}
+
+	grid_time = 0;
+
+	//label7.Caption = inttostr(stat_r_mem); mem_arq
+
+	num_rows = 0;
+	row_idx = 0;
+
+	for (snd_ch = 0; snd_ch < 4; snd_ch++)
+	{
+		for (i = 0; i < port_num; i++)
+		{
+			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
+				num_rows++;
+		}
+	}
+
+	if (num_rows == 0)
+	{
+		sessionTable->clearContents();
+		sessionTable->setRowCount(0);
+		sessionTable->setRowCount(1);
+	}
+	else
+		sessionTable->setRowCount(num_rows);
+
+
+	for (snd_ch = 0; snd_ch < 4; snd_ch++)
+	{
+		for (i = 0; i < port_num; i++)
+		{
+			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
+			{
+				switch (AX25Port[snd_ch][i].status)
+				{
+				case STAT_NO_LINK:
+
+					msg = "No link";
+					break;
+
+				case STAT_LINK:
+
+					msg = "Link";
+					break;
+
+				case STAT_CHK_LINK:
+
+					msg = "Chk link";
+					break;
+
+				case STAT_WAIT_ANS:
+
+					msg = "Wait ack";
+					break;
+
+				case STAT_TRY_LINK:
+
+					msg = "Try link";
+					break;
+
+				case STAT_TRY_UNLINK:
+
+					msg = "Try unlink";
+				}
+
+
+				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall);
+				sessionTable->setItem(row_idx, 0, item);
+
+				item = new QTableWidgetItem(AX25Port[snd_ch][i].kind);
+				sessionTable->setItem(row_idx, 11, item);
+
+				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall);
+				sessionTable->setItem(row_idx, 1, item);
+
+				item = new QTableWidgetItem(msg);
+				sessionTable->setItem(row_idx, 2, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt));
+				sessionTable->setItem(row_idx, 3, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte));
+				sessionTable->setItem(row_idx, 4, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt));
+				sessionTable->setItem(row_idx, 5, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte));
+				sessionTable->setItem(row_idx, 6, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc));
+				sessionTable->setItem(row_idx, 7, item);
+
+				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count));
+				sessionTable->setItem(row_idx, 8, item);
+
+				if (grid_timer != upd_time)
+					grid_timer++;
+				else
+				{
+					grid_timer = 0;
+					speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time);
+					speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time);
+
+					item = new QTableWidgetItem(QString::number(speed_tx));
+					sessionTable->setItem(row_idx, 9, item);
+
+					item = new QTableWidgetItem(QString::number(speed_rx));
+					sessionTable->setItem(row_idx, 10, item);
+
+					AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte;
+					AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte;	
+				}
+
+				row_idx++;
+			}
+		}
+	}
+}
+
+// "Copy on Select" Code
+
+void QtSoundModem::onTEselectionChanged()
+{
+	QTextEdit * x = static_cast<QTextEdit*>(QObject::sender());
+	x->copy();
+}
+
+#define ConstellationHeight 121
+#define ConstellationWidth 121
+#define PLOTRADIUS 60
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count)
+{
+	// Subroutine to update bmpConstellation plot for PSK modes...
+	// Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014)
+
+	float dblPhaseError;
+	float dblPhaseErrorSum = 0;
+	float intP = 0;
+	float dblRad = 0;
+	float dblAvgRad = 0;
+	float dbPhaseStep;
+	float MagMax = 0;
+	float dblPlotRotation = 0;
+
+	int i, intQuality;
+
+	int intX, intY;
+	int yCenter = (ConstellationHeight - 2) / 2;
+	int xCenter = (ConstellationWidth - 2) / 2;
+
+	if (Count == 0)
+		return 0;
+
+	if (nonGUIMode == 0)
+	{
+		Constellation[chan]->fill(black);
+
+		for (i = 0; i < 120; i++)
+		{
+			Constellation[chan]->setPixel(xCenter, i, cyan);
+			Constellation[chan]->setPixel(i, xCenter, cyan);
+		}
+	}
+	dbPhaseStep = 2 * M_PI / intPSKPhase;
+
+	for (i = 1; i < Count; i++)  // Don't plot the first phase (reference)
+	{
+		MagMax = MAX(MagMax, Mags[i]); // find the max magnitude to auto scale
+		dblAvgRad += Mags[i];
+	}
+
+	dblAvgRad = dblAvgRad / Count; // the average radius
+
+	for (i = 0; i < Count; i++)
+	{
+		dblRad = PLOTRADIUS * Mags[i] / MagMax; //  scale the radius dblRad based on intMag
+		intP = round((Phases[i]) / dbPhaseStep);
+
+		// compute the Phase error
+
+		dblPhaseError = fabsf(Phases[i] - intP * dbPhaseStep); // always positive and < .5 *  dblPhaseStep
+		dblPhaseErrorSum += dblPhaseError;
+
+		if (nonGUIMode == 0)
+		{
+			intX = xCenter + dblRad * cosf(dblPlotRotation + Phases[i]);
+			intY = yCenter + dblRad * sinf(dblPlotRotation + Phases[i]);
+
+			if (intX > 0 && intY > 0)
+				if (intX != xCenter && intY != yCenter)
+					Constellation[chan]->setPixel(intX, intY, yellow);
+		}
+	}
+
+	dblAvgRad = dblAvgRad / Count; // the average radius
+
+	intQuality = MAX(0, ((100 - 200 * (dblPhaseErrorSum / (Count)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM
+
+	if (nonGUIMode == 0)
+	{
+		char QualText[64];
+		sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality);
+		QualLabel[chan]->setText(QualText);
+		constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan]));
+	}
+	return intQuality;
+}
+
+
+
diff --git a/QtSoundModem.h b/QtSoundModem.h
index b02f9c2..a759f4b 100644
--- a/QtSoundModem.h
+++ b/QtSoundModem.h
@@ -43,6 +43,7 @@ private slots:
 	void updateFont();
 	void MinimizetoTray();
 	void TrayActivated(QSystemTrayIcon::ActivationReason reason);
+	void StatsTimer();
 	void MyTimerSlot();
 	void returnPressed();
 	void clickedSlotI(int i);
@@ -77,6 +78,7 @@ private slots:
 	void StartWatchdog();
 	void StopWatchdog();
 	void PTTWatchdogExpired();
+	void showRequest(QByteArray Data);
 	void clickedSlot();
 	void startCWIDTimerSlot();
 	void setWaterfallImage();
@@ -124,3 +126,25 @@ protected:
 #define WaterfallTotalPixels WaterfallDisplayPixels + WaterfallHeaderPixels
 #define WaterfallImageHeight (WaterfallTotalPixels + WaterfallTotalPixels)
 
+
+class serialThread : public QThread
+{
+	Q_OBJECT
+
+public:
+	void run() Q_DECL_OVERRIDE;
+	void startSlave(const QString &portName, int waitTimeout, const QString &response);
+
+signals:
+	void request(const QByteArray &s);
+	void error(const QString &s);
+	void timeout(const QString &s);
+
+private:
+	QString portName;
+	QString response;
+	int waitTimeout;
+	QMutex mutex;
+	bool quit;
+};
+
diff --git a/QtSoundModem.pro b/QtSoundModem.pro
index a02654c..796bac4 100644
--- a/QtSoundModem.pro
+++ b/QtSoundModem.pro
@@ -44,8 +44,8 @@ SOURCES += ./audio.c \
     ./ofdm.c \
     ./pktARDOP.c \
      ./BusyDetect.c \
-	 ./DW9600.c
-
+	 ./DW9600.c \
+	./6pack.cpp
 
 
 FORMS += ./calibrateDialog.ui \
diff --git a/QtSoundModem.pro.user b/QtSoundModem.pro.user
new file mode 100644
index 0000000..69ff052
--- /dev/null
+++ b/QtSoundModem.pro.user
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 11.0.3, 2024-08-15T14:29:24. -->
+<qtcreator>
+ <data>
+  <variable>EnvironmentId</variable>
+  <value type="QByteArray">{6e41d268-43e9-43ac-b8fa-a3c083d547a3}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="qlonglong">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="bool" key="EditorConfiguration.UseIndenter">false</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+   <value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
+   <value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap">
+   <valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
+    <value type="bool" key="AutoTest.Framework.Boost">true</value>
+    <value type="bool" key="AutoTest.Framework.CTest">false</value>
+    <value type="bool" key="AutoTest.Framework.Catch">true</value>
+    <value type="bool" key="AutoTest.Framework.GTest">true</value>
+    <value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
+    <value type="bool" key="AutoTest.Framework.QtTest">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
+   <value type="int" key="AutoTest.RunAfterBuild">0</value>
+   <value type="bool" key="AutoTest.UseGlobal">true</value>
+   <valuemap type="QVariantMap" key="ClangTools">
+    <value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
+    <value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
+    <value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
+    <value type="int" key="ClangTools.ParallelJobs">0</value>
+    <value type="bool" key="ClangTools.PreferConfigFile">true</value>
+    <valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
+    <valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
+    <valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
+    <value type="bool" key="ClangTools.UseGlobalSettings">true</value>
+   </valuemap>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="DeviceType">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop (x86-darwin-generic-mach_o-64bit)</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop (x86-darwin-generic-mach_o-64bit)</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{a36d9ffa-38ce-4dfc-9820-5a456a9dc53d}</value>
+   <value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
+   <value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="int" key="EnableQmlDebugging">0</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Release</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="int" key="QtQuickCompiler">0</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
+    <value type="int" key="EnableQmlDebugging">0</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Profile</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+     </valuemap>
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="int" key="QtQuickCompiler">0</value>
+    <value type="int" key="SeparateDebugInfo">0</value>
+   </valuemap>
+   <value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
+    <value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <valuelist type="QVariantList" key="CustomOutputParsers"/>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/Volumes/Source/QT/QtSoundModem/QtSoundModem.pro</value>
+    <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/Volumes/Source/QT/QtSoundModem/QtSoundModem.pro</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory.default">/Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug/QtSoundModem.app/Contents/MacOS</value>
+   </valuemap>
+   <value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="qlonglong">1</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">22</value>
+ </data>
+ <data>
+  <variable>Version</variable>
+  <value type="int">22</value>
+ </data>
+</qtcreator>
diff --git a/QtSoundModem.pro~ b/QtSoundModem.pro~
new file mode 100644
index 0000000..a02654c
--- /dev/null
+++ b/QtSoundModem.pro~
@@ -0,0 +1,64 @@
+
+QT += core gui
+QT += network
+QT += serialport
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = QtSoundModem
+TEMPLATE = app
+
+
+HEADERS += ./UZ7HOStuff.h \
+    ./QtSoundModem.h \
+    ./tcpCode.h
+
+SOURCES += ./audio.c \
+    ./pulse.c \
+    ./ax25.c \
+    ./ax25_demod.c \
+    ./ax25_l2.c \
+    ./ax25_mod.c \
+    ./Config.cpp \
+    ./kiss_mode.c \
+    ./main.cpp \
+    ./QtSoundModem.cpp \
+    ./ShowFilter.cpp \
+    ./SMMain.c \
+    ./sm_main.c \
+    ./UZ7HOUtils.c \
+    ./ALSASound.c \
+        ./ax25_agw.c \
+        ./berlekamp.c \
+        ./galois.c \
+        ./rs.c \
+       ./rsid.c \
+	./il2p.c \
+    ./tcpCode.cpp \
+    ./ax25_fec.c \
+    ./RSUnit.c \
+	./ARDOPC.c \
+    ./ardopSampleArrays.c \
+    ./SoundInput.c \
+    ./Modulate.c \
+    ./ofdm.c \
+    ./pktARDOP.c \
+     ./BusyDetect.c \
+	 ./DW9600.c
+
+
+
+FORMS += ./calibrateDialog.ui \
+    ./devicesDialog.ui \
+    ./filterWindow.ui \
+    ./ModemDialog.ui \
+    ./QtSoundModem.ui
+
+RESOURCES += QtSoundModem.qrc
+RC_ICONS = QtSoundModem.ico
+
+QMAKE_CFLAGS += -g
+#QMAKE_LFLAGS += -lasound -lpulse-simple -lpulse -lfftw3f
+QMAKE_LIBS += -lasound -lfftw3f -ldl
+
+
diff --git a/QtSoundModem.vcxproj b/QtSoundModem.vcxproj
index 765fd37..1a66795 100644
--- a/QtSoundModem.vcxproj
+++ b/QtSoundModem.vcxproj
@@ -382,6 +382,7 @@
     </QtUic>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="6pack.cpp" />
     <ClCompile Include="ARDOPC.c" />
     <ClCompile Include="berlekamp.c" />
     <ClCompile Include="BusyDetect.c" />
diff --git a/QtSoundModem.vcxproj.filters b/QtSoundModem.vcxproj.filters
index 2d05c65..94b092a 100644
--- a/QtSoundModem.vcxproj.filters
+++ b/QtSoundModem.vcxproj.filters
@@ -140,6 +140,9 @@
     <ClCompile Include="berlekamp.c">
       <Filter>Generated Files</Filter>
     </ClCompile>
+    <ClCompile Include="6pack.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <QtMoc Include="QtSoundModem.h">
diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user
index 3d36629..e2d168a 100644
--- a/QtSoundModem.vcxproj.user
+++ b/QtSoundModem.vcxproj.user
@@ -20,15 +20,15 @@
     <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
   </PropertyGroup>
   <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <QtLastBackgroundBuild>2024-06-21T13:50:20.1736205Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2024-10-02T12:37:32.3312444Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="QtSettings">
-    <QtLastBackgroundBuild>2024-06-21T13:50:20.3245934Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2024-10-02T12:37:32.7461423Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <QtLastBackgroundBuild>2024-06-21T13:50:20.4555992Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2024-10-02T12:37:33.4644113Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="QtSettings">
-    <QtLastBackgroundBuild>2024-06-21T13:50:20.6366033Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2024-10-02T12:37:33.9800504Z</QtLastBackgroundBuild>
   </PropertyGroup>
 </Project>
\ No newline at end of file
diff --git a/SMMain.c b/SMMain.c
index bf93779..17917bc 100644
--- a/SMMain.c
+++ b/SMMain.c
@@ -29,6 +29,42 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 #include <errno.h>
 #include <stdint.h>   
 
+
+void platformInit();
+void RsCreate();
+void detector_init();
+void KISS_init();
+void ax25_init();
+void init_raduga();
+void il2p_init(int il2p_debug);
+void SoundFlush();
+void BufferFull(short * Samples, int nSamples);
+void PollReceivedSamples();
+void chk_dcd1(int snd_ch, int buf_size);
+void make_core_BPF(UCHAR snd_ch, short freq, short width);
+void modulator(UCHAR snd_ch, int buf_size);
+void sendAckModeAcks(int snd_ch);
+void PktARDOPEncode(UCHAR * Data, int Len, int Chan);
+void RUHEncode(unsigned char * Data, int Len, int chan);
+void sendRSID(int Chan, int dropTX);
+void SetupGPIOPTT();
+int stricmp(const unsigned char * pStr1, const unsigned char *pStr2);
+int gpioInitialise(void);
+int OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits);
+void HAMLIBSetPTT(int PTTState);
+void FLRigSetPTT(int PTTState);
+void StartWatchdog();
+void StopWatchdog();
+void gpioWrite(unsigned gpio, unsigned level);
+BOOL WriteCOMBlock(int fd, char * Block, int BytesToWrite);
+void COMSetDTR(int fd);
+void COMSetRTS(int fd);
+void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag);
+size_t write(int fd, void * buf, size_t count);
+int close(int fd);
+void SendMgmtPTT(int snd_ch, int PTTState);
+
+
 BOOL KISSServ;
 int KISSPort;
 
@@ -367,13 +403,13 @@ extern int intLastStop;
 void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float *  dblAVGSignalPerBin, float *  dblAVGBaselinePerBin);
 
 
-BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop)        // this only called while searching for leader ...once leader detected, no longer called.
+BOOL SMBusyDetect3(int Chan, float * dblMag, int intStart, int intStop)        // this only called while searching for leader ...once leader detected, no longer called.
 {
 	// First sort signals and look at highes signals:baseline ratio..
 
 	float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide;
 	float dblSlowAlpha = 0.2f;
-	float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0;
+	static float dblAvgStoNNarrow[4] = { 0 }, dblAvgStoNWide[4] = {0};
 	int intNarrow = 8;  // 8 x 11.72 Hz about 94 z
 	int intWide = ((intStop - intStart) * 2) / 3; //* 0.66);
 	int blnBusy = FALSE;
@@ -384,15 +420,16 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop)        // this onl
 
 	SMSortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow);
 
+	// Shouldn't dblAvgStoNNarrow, dblAvgStoNWide be static ??????
+
+
 	if (intLastStart == intStart && intLastStop == intStop)
-		dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
+		dblAvgStoNNarrow[Chan] = (1 - dblSlowAlpha) * dblAvgStoNNarrow[Chan] + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
 	else
 	{
 		// This initializes the Narrow average after a bandwidth change
 
-		dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
-		intLastStart = intStart;
-		intLastStop = intStop;
+		dblAvgStoNNarrow[Chan] = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow;
 	}
 
 	// Wide band (66% of current bandwidth)
@@ -400,12 +437,12 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop)        // this onl
 	SMSortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide);
 
 	if (intLastStart == intStart && intLastStop == intStop)
-		dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide;
+		dblAvgStoNWide[Chan] = (1 - dblSlowAlpha) * dblAvgStoNWide[Chan] + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide;
 	else
 	{
 		// This initializes the Wide average after a bandwidth change
 
-		dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide;
+		dblAvgStoNWide[Chan] = dblAVGSignalPerBinWide / dblAVGBaselineWide;
 		intLastStart = intStart;
 		intLastStop = intStop;
 	}
@@ -413,7 +450,7 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop)        // this onl
 	// Preliminary calibration...future a function of bandwidth and BusyDet.
 
 
-	blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.02 * BusyDet4th));
+	blnBusy = (dblAvgStoNNarrow[Chan] > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide[Chan] > (5 + 0.02 * BusyDet4th));
 
 //	if (BusyDet == 0)
 //		blnBusy = FALSE;		// 0 Disables check ?? Is this the best place to do this?
@@ -452,7 +489,7 @@ void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumB
 	*dblAVGBaselinePerBin = dblSum2 / (intStopBin - intStartBin - intNumBins - 1);
 }
 
-
+extern void updateDCD(int Chan, BOOL State);
 
 void SMUpdateBusyDetector(int LR, float * Real, float *Imag)
 {
@@ -465,10 +502,8 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag)
 	static BOOL blnLastBusyStatus[4];
 
 	float dblMagAvg = 0;
-	int intTuneLineLow, intTuneLineHi, intDelta;
 	int i, chan;
 
-	return;
 
 	if (Now - LastBusyCheck < 100)	// ??
 		return;
@@ -487,6 +522,9 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag)
 		Low = tx_freq[chan] - txbpf[chan] / 2;
 		High = tx_freq[chan] + txbpf[chan] / 2;
 
+//		Low = tx_freq[chan] - 0.5*rx_shift[chan];
+//		High = tx_freq[chan] + 0.5*rx_shift[chan];
+
 		// BinSize is width of each fft bin in Hz
 
 		Start = (Low / BinSize);		// First and last bins to process
@@ -499,20 +537,18 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag)
 			dblMagAvg += dblMag[i];
 		}
 
-		blnBusyStatus = SMBusyDetect3(dblMag, Start, End);
+		blnBusyStatus = SMBusyDetect3(chan, dblMag, Start, End);
 
 		if (blnBusyStatus && !blnLastBusyStatus[chan])
 		{
 			Debugprintf("Ch %d Busy True", chan);
+			updateDCD(chan, TRUE);
 		}
 		else if (blnLastBusyStatus[chan] && !blnBusyStatus)
 		{
 			Debugprintf("Ch %d Busy False", chan);
+			updateDCD(chan, FALSE);
 		}
-		//    stcStatus.Text = "FALSE"
-		//    queTNCStatus.Enqueue(stcStatus)
-		//    'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss"))
-
 		blnLastBusyStatus[chan] = blnBusyStatus;
 	}
 }
@@ -1222,6 +1258,7 @@ void CM108_set_ptt(int PTTState)
 float amplitudes[4] = { 32000, 32000, 32000, 32000 };
 extern float amplitude;
 void startpttOnTimer();
+extern void UpdatePTTStats(int Chan, int State);
 
 void RadioPTT(int snd_ch, BOOL PTTState)
 {
@@ -1240,6 +1277,15 @@ void RadioPTT(int snd_ch, BOOL PTTState)
 		StopWatchdog();
 	}
 
+	if ((PTTMode & PTTHOST))
+	{
+		// Send PTT ON/OFF to any mgmt connections
+
+		SendMgmtPTT(snd_ch, PTTState);
+	}
+
+	UpdatePTTStats(snd_ch, PTTState);
+
 #ifdef __ARM_ARCH
 	if (useGPIO)
 	{
@@ -1306,6 +1352,8 @@ void RadioPTT(int snd_ch, BOOL PTTState)
 				COMClearRTS(hPTTDevice);
 		}
 	}
+
+				
 	startpttOnTimer();
 
 }
diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h
index c4146ff..563daf9 100644
--- a/UZ7HOStuff.h
+++ b/UZ7HOStuff.h
@@ -4,8 +4,8 @@
 //	 My port of UZ7HO's Soundmodem
 //
 
-#define VersionString "0.0.0.72"
-#define VersionBytes {0, 0, 0, 72}
+#define VersionString "0.0.0.73"
+#define VersionBytes {0, 0, 0, 73}
 
 //#define LOGTX
 //#define LOGRX
@@ -181,7 +181,7 @@
 //		Improve reliability of waterfall update
 //		Report and set fx.25 and il2p flags to/from BPQ
 
-// .72	Fix IL2P for RUH modems
+// .72	Fix IL2P for RUH modems						// July 2024
 //		Fix crash when closing in non-gui mode
 //		Fix loop in chk_dcd1
 //		Change method of timing PTT
@@ -192,6 +192,11 @@
 
 //		Change phase map of QPSK3600 mode to match Nino TNC
 
+//.73	Fix QPSK3600 RX Filters
+//		Add Mgmt Interface (Beta 2)
+//		Use ARDOP Busy detector (Beta 3)
+//		Various fixes to AGW interface (Beta 4)
+
 
 
 // As far as I can see txtail is only there to make sure all bits get through the tx filter,
@@ -317,6 +322,15 @@ typedef struct TKISSMode_t
 
 } TKISSMode;
 
+typedef struct MgmtMode_t
+{
+	void * Socket;				// Used as a key
+	char Msg[512];			// Received message 
+	int Len;
+	int BPQPort[4];			// BPQ port for each modem
+
+} TMgmtMode;
+
 typedef struct  TMChannel_t
 {
 
@@ -461,7 +475,7 @@ typedef struct AGWUser_t
 {
 	void *socket;
 	string * data_in;
-	TStringList AGW_frame_buf;
+//	TStringList AGW_frame_buf;
 	boolean	Monitor;
 	boolean	Monitor_raw;
 	boolean reportFreqAndModem;			// Can report modem and frequency to host
@@ -534,6 +548,7 @@ typedef struct TAX25Port_t
 #define PTTCM108	8
 #define PTTHAMLIB	16
 #define PTTFLRIG	32
+#define PTTHOST		128				// May be combined with others
 
 // Status flags
 
@@ -677,9 +692,9 @@ extern int SendSize;
 #define MODEM_Q2400_BPF_TAP 256 //256
 #define MODEM_Q2400_LPF_TAP 128  //128
  //
-#define MODEM_Q3600_BPF 1800
-#define MODEM_Q3600_TXBPF 2000
-#define MODEM_Q3600_LPF 600
+#define MODEM_Q3600_BPF 3600
+#define MODEM_Q3600_TXBPF 3000
+#define MODEM_Q3600_LPF 1350
 #define MODEM_Q3600_BPF_TAP 256
 #define MODEM_Q3600_LPF_TAP 128
  //
@@ -1011,6 +1026,9 @@ extern int IPOLL[4];
 extern int maxframe[4];
 extern int TXFrmMode[4];
 
+extern int bytes[4];
+extern int bytes2mins[4];
+
 extern char MyDigiCall[4][512];
 extern char exclude_callsigns[4][512];
 extern char exclude_APRS_frm[4][512];
diff --git a/Waveout.c b/Waveout.c
index 22f05d3..fba668f 100644
--- a/Waveout.c
+++ b/Waveout.c
@@ -44,6 +44,11 @@ void printtick(char * msg);
 void PollReceivedSamples();
 short * SoundInit();
 void StdinPollReceivedSamples();
+extern void WriteDebugLog(char * Mess);
+extern void UDPPollReceivedSamples();
+extern void QSleep(int ms);
+extern void ProcessNewSamples(short * Samples, int nSamples);
+extern void sendSamplestoStdout(short * Samples, int nSamples);
 
 #include <math.h>
 
@@ -57,7 +62,7 @@ void GetSoundDevices();
 #define MaxReceiveSize 2048		// Enough for 9600
 #define MaxSendSize 4096
 
-short buffer[2][MaxSendSize * 2];		// Two Transfer/DMA buffers of 0.1 Sec  (x2 for Stereo)
+short buffer[4][MaxSendSize * 2];		// Two Transfer/DMA buffers of 0.1 Sec  (x2 for Stereo)
 short inbuffer[5][MaxReceiveSize * 2];	// Input Transfer/ buffers of 0.1 Sec (x2 for Stereo)
 
 extern short * DMABuffer;
@@ -102,10 +107,12 @@ WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 2, 12000, 48000, 4, 16, 0 };
 HWAVEOUT hWaveOut = 0;
 HWAVEIN hWaveIn = 0;
 
-WAVEHDR header[2] =
+WAVEHDR header[4] =
 {
 	{(char *)buffer[0], 0, 0, 0, 0, 0, 0, 0},
-	{(char *)buffer[1], 0, 0, 0, 0, 0, 0, 0}
+	{(char *)buffer[1], 0, 0, 0, 0, 0, 0, 0},
+	{(char *)buffer[2], 0, 0, 0, 0, 0, 0, 0},
+	{(char *)buffer[3], 0, 0, 0, 0, 0, 0, 0}
 };
 
 WAVEHDR inheader[5] =
@@ -155,12 +162,6 @@ VOID __cdecl Debugprintf(const char * format, ...)
 
 void platformInit()
 {
-	TIMECAPS tc;
-	unsigned int     wTimerRes;
-	DWORD	t, lastt = 0;
-	int i = 0;
-
-
 	_strupr(CaptureDevice);
 	_strupr(PlaybackDevice);
 
@@ -224,20 +225,20 @@ FILE * wavfp1;
 
 BOOL DMARunning = FALSE;		// Used to start DMA on first write
 
-BOOL SeeIfCardBusy()
-{
-	if ((header[!Index].dwFlags & WHDR_DONE))
-		return 0;
-
-	return 1;
-}
-
 extern void sendSamplestoUDP(short * Samples, int nSamples, int Port);
 
 extern int UDPClientPort;
 
 short * SendtoCard(unsigned short * buf, int n)
 {
+	int NextBuffer = Index;
+	char Msg[80];
+
+	NextBuffer++;
+
+	if (NextBuffer > 3)
+		NextBuffer = 0;
+
 	if (SoundMode == 3)			// UDP
 	{
 		sendSamplestoUDP(buf, n, UDPClientPort);
@@ -256,15 +257,17 @@ short * SendtoCard(unsigned short * buf, int n)
 	waveOutPrepareHeader(hWaveOut, &header[Index], sizeof(WAVEHDR));
 	waveOutWrite(hWaveOut, &header[Index], sizeof(WAVEHDR));
 
-	// wait till previous buffer is complete
+	// wait till next buffer is free
 
-	while (!(header[!Index].dwFlags & WHDR_DONE))
+	while (!(header[NextBuffer].dwFlags & WHDR_DONE))
 	{
 		txSleep(5);				// Run buckground while waiting 
 	}
 
-	waveOutUnprepareHeader(hWaveOut, &header[!Index], sizeof(WAVEHDR));
-	Index = !Index;
+	waveOutUnprepareHeader(hWaveOut, &header[NextBuffer], sizeof(WAVEHDR));
+	Index = NextBuffer;
+
+	sprintf(Msg, "TX Buffer %d", NextBuffer);
 
 	return &buffer[Index][0];
 }
@@ -365,7 +368,7 @@ int onlyMixSnoop = 0;
 
 int InitSound(BOOL Report)
 {
-	int i, t, ret;
+	int i, ret;
 
 	if (SoundMode == 4)
 	{
@@ -400,6 +403,8 @@ int InitSound(BOOL Report)
 
 	header[0].dwFlags = WHDR_DONE;
 	header[1].dwFlags = WHDR_DONE;
+	header[2].dwFlags = WHDR_DONE;
+	header[3].dwFlags = WHDR_DONE;
 
 	if (strlen(PlaybackDevice) <= 2)
 		PlayBackIndex = atoi(PlaybackDevice);
@@ -723,11 +728,15 @@ void SoundFlush()
 	SendtoCard(buffer[Index], Number);
 
 	//	Wait for all sound output to complete
-	
+
 	while (!(header[0].dwFlags & WHDR_DONE))
 		txSleep(10);
 	while (!(header[1].dwFlags & WHDR_DONE))
 		txSleep(10);
+	while (!(header[2].dwFlags & WHDR_DONE))
+		txSleep(10);
+	while (!(header[3].dwFlags & WHDR_DONE))
+		txSleep(10);
 
 	// I think we should turn round the link here. I dont see the point in
 	// waiting for MainPoll
@@ -922,6 +931,7 @@ BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite)
 	if ((!fWriteStat) || (BytesToWrite != BytesWritten))
 	{
 		int Err = GetLastError();
+		Debugprintf("Serial Write Error %d", Err);
 		ClearCommError(fd, &ErrorFlags, &ComStat);
 		return FALSE;
 	}
diff --git a/audio.c b/audio.c
index cd2fa16..7e062ec 100644
--- a/audio.c
+++ b/audio.c
@@ -40,6 +40,7 @@
 #endif
 
 void Debugprintf(const char * format, ...);
+void Sleep(int mS);
 
 extern int Closing;
 
diff --git a/ax25.c b/ax25.c
index 4ae9083..32933cd 100644
--- a/ax25.c
+++ b/ax25.c
@@ -29,10 +29,20 @@ __declspec(dllimport) unsigned short __stdcall ntohs(__in unsigned short hostsho
 
 #else
 
+#include <stdint.h>
+
+uint32_t htonl(uint32_t hostlong);
+uint16_t htons(uint16_t hostshort);
+uint32_t ntohl(uint32_t netlong);
+uint16_t ntohs(uint16_t netshort);
+
 #define strtok_s strtok_r
 #include <stddef.h>
 #endif 
 
+void set_DM(int snd_ch, Byte * path);
+void  rst_values(TAX25Port * AX25Sess);
+
 void decode_frame(Byte * frame, int len, Byte * path, string * data,
 	Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id,
 	Byte *  rpt, Byte * pf, Byte * cr);
@@ -244,6 +254,9 @@ int maxframe[4] = { 0,0,0,0 };
 int TXFrmMode[4] = { 0,0,0,0 };
 int max_frame_collector[4] = { 0,0,0,0 };
 
+int bytes[4] = { 0,0,0,0 };
+int bytes2mins[4] = { 0,0,0,0 };
+
 char MyDigiCall[4][512] = { "","","","" };
 char exclude_callsigns[4][512] = { "","","","" };
 char exclude_APRS_frm[4][512] = { "","","","" };
@@ -1729,7 +1742,7 @@ int  is_excluded_frm(int snd_ch, int f_id, string * data)
 
 
 
-int number_digi(string path)
+int number_digi(char * path)
 {
 	int n = 0;
 
diff --git a/ax25_agw.c b/ax25_agw.c
index 12526ff..7f6fdae 100644
--- a/ax25_agw.c
+++ b/ax25_agw.c
@@ -177,7 +177,7 @@ void AGW_del_socket(void * socket)
 	if (AGW == NULL)
 		return;
 
-	Clear(&AGW->AGW_frame_buf);
+//	Clear(&AGW->AGW_frame_buf);
 	freeString(AGW->data_in);
 	AGW->Monitor = 0;
 	AGW->Monitor_raw = 0;
@@ -190,9 +190,7 @@ void AGW_add_socket(void * socket)
 {
 	AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t));			// One Client
 	
-
 	AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *));
-	
 	AGWUsers[AGWConCount++] = User;
 
 	User->data_in = newString();
@@ -259,7 +257,7 @@ string * AGW_R_Frame()
 
 string * AGW_X_Frame(char * CallFrom,  UCHAR reg_call)
 {
-	string * Msg = AGW_frame_header(0, 'x', 0, CallFrom, "", 1);
+	string * Msg = AGW_frame_header(0, 'X', 0, CallFrom, "", 1);
 
 	stringAdd(Msg, (UCHAR *)&reg_call, 1);
 
@@ -276,13 +274,16 @@ string * AGW_G_Frame()
 	
 	for (int i = 0; i < 4; i++)
 	{
-		Ports[0]++;
 		if (soundChannel[i])
+		{
+			Ports[0]++;
 			sprintf(portMsg, "Port%c with SoundCard Ch %c;", Ports[0], 'A' + i);
-		else
-			sprintf(portMsg, "Port%c Disabled;", Ports[0]);
+			strcat(Ports, portMsg);
+		}
+//		else
+//			sprintf(portMsg, "Port%c Disabled;", Ports[0]);
 
-		strcat(Ports, portMsg);
+;
 	}
 
 
@@ -294,7 +295,6 @@ string * AGW_G_Frame()
 };
 
 
-
 string * AGW_Gs_Frame(int port, Byte * port_info, int Len)
 {
 	string * Msg;
@@ -511,12 +511,50 @@ void on_AGW_R_frame(AGWUser * AGW)
 int refreshModems = 0;
 
 
+/*
+
++ 00 On air baud rate(0 = 1200 / 1 = 2400 / 2 = 4800 / 3 = 9600�)
++ 01 Traffic level(if 0xFF the port is not in autoupdate mode)
++ 02 TX Delay
++ 03 TX Tail
++ 04 Persist
++ 05 SlotTime
++ 06 MaxFrame
++ 07 How Many connections are active on this port
++ 08 LSB Low Word
++ 09 MSB Low Word
++ 10 LSB High Word
++ 11 MSB High Word HowManyBytes(received in the last 2 minutes) as a 32 bits(4 bytes) integer.Updated every two minutes.
+*/
+
 void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
 {
 	// QTSM with a data field is used by QtSM to set/read Modem Params
 
 	Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
 	int Len = 12;
+	int Port = Frame->Port;
+
+	// KD6YAM wants the info to be correct. BPQ used 24, 3, 100 as a signature but could use the Version.
+	// For now I'll fill in the rest
+
+	info[2] = txdelay[Port] / 10;
+	info[3] = txtail[Port] / 10;
+	info[4] = persist[Port];
+	info[5] = slottime[Port];
+	info[6] = maxframe[Port];
+	info[7] = 0;
+
+	int i = 0;
+	while (i < port_num)
+	{
+		if (AX25Port[Port][i].status != STAT_NO_LINK)
+			info[7]++;
+
+		i++;
+	}
+
+	memcpy(&info[8], &bytes2mins[Port], 4);
 
 	if (Frame->DataLength == 32)
 	{
@@ -1360,7 +1398,6 @@ void AGW_Report_Modem_Change(int port)
 
 	int i;
 	AGWUser * AGW;
-	string * pkt;
 
 	if (soundChannel[port] == 0)		// Not in use
 		return;
@@ -1537,3 +1574,16 @@ void AGW_frame_analiz(AGWUser *  AGW)
 		Debugprintf("AGW %c", Frame->DataKind);
 	}
 }
+
+void doAGW2MinTimer()
+{
+	for (int n = 0; n < 4; n++)
+	{
+		if (soundChannel[n] == 0)	// Channel not used
+			continue;
+
+		bytes2mins[n] = bytes[n];
+		bytes[n] = 0;
+	}
+}
+
diff --git a/ax25_demod.c b/ax25_demod.c
index 06d66f5..a05f39f 100644
--- a/ax25_demod.c
+++ b/ax25_demod.c
@@ -27,6 +27,14 @@ extern word MEMRecovery[5];
 
 void  make_rx_frame_FX25(int snd_ch, int rcvr_nr, int emph, string * data);
 string * memory_ARQ(TStringList * buf, string * data);
+void CreateStringList(TStringList * List);
+void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag);
+void KISS_on_data_out(int port, string * frame, int TX);
+void updateDCD(int Chan, boolean State);
+void Frame_Optimize(TAX25Port * AX25Sess, TStringList * buf);
+void RX2TX(int snd_ch);
+int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size);
+void il2p_rec_bit(int chan, int subchan, int slice, int dbit);
 
 float GuessCentreFreq(int i);
 void ProcessRXFrames(int snd_ch);
@@ -374,6 +382,7 @@ void chk_dcd1(int snd_ch, int buf_size)
 
 	if (lastDCDState[snd_ch] != dcd_bit_sync[snd_ch])
 	{
+		updateDCD(snd_ch, dcd_bit_sync[snd_ch]);
 		updateDCD(snd_ch, dcd_bit_sync[snd_ch]);
 		lastDCDState[snd_ch] = dcd_bit_sync[snd_ch];
 	}
@@ -4258,7 +4267,7 @@ void ProcessRXFrames(int snd_ch)
 
 	// Work out which decoder and which emph settings worked. 
 
-	if (snd_ch < 0 || snd_ch >3)
+	if (snd_ch < 0 || snd_ch > 3)
 		return;
 
 	if (detect_list[snd_ch].Count > 0)		// no point if nothing decoded
diff --git a/ax25_l2.c b/ax25_l2.c
index a99a613..8efcceb 100644
--- a/ax25_l2.c
+++ b/ax25_l2.c
@@ -28,6 +28,16 @@ extern int RSID_SetModem[4];
 extern int needRSID[4];
 extern int needRSID[4];
 
+BOOL useKISSControls = 0;
+
+#define FEND 0xc0
+#define FESC 0xDB
+#define TFEND 0xDC
+#define TFESC 0xDD
+#define KISS_ACKMODE 0x0C
+#define KISS_DATA 0
+#define QTSMKISSCMD 7
+
 
 /*
 
@@ -60,6 +70,23 @@ string * make_frame(string * data, Byte * path, Byte  pid, Byte nr, Byte ns, Byt
 void rst_t3(TAX25Port * AX25Sess);
 
 TAX25Port * get_user_port(int snd_ch, Byte * path);
+void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag);
+boolean is_last_digi(Byte *path);
+int is_excluded_call(int snd_ch, unsigned char * path);
+boolean is_correct_path(Byte * path, Byte pid);
+void KISS_on_data_out(int port, string * frame, int TX);
+void ax25_info_init(TAX25Port * AX25Sess);
+void  clr_frm_win(TAX25Port * AX25Sess);
+void decode_frame(Byte * frame, int len, Byte * path, string * data,
+	Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id,
+	Byte *  rpt, Byte * pf, Byte * cr);
+void ax25_info_init(TAX25Port * AX25Sess);
+void AGW_AX25_disc(TAX25Port * AX25Sess, Byte mode);
+void AGW_AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode);
+int number_digi(char * path);
+void AGW_AX25_data_in(void  * socket, int snd_ch, int PID, Byte * path, string * data);
+
+
 
 void  inc_frack(TAX25Port * AX25Sess)
 {
@@ -1100,7 +1127,8 @@ void on_FRMR(void * socket, TAX25Port * AX25Sess, Byte * path)
 	{
 		AX25Sess->info.stat_end_ses = time(NULL);
 
-		AGW_AX25_disc(socket, AX25Sess->snd_ch, MODE_OTHER, path);
+		AGW_AX25_disc(AX25Sess, MODE_OTHER);
+
 		write_ax25_info(AX25Sess);
 	}
 
@@ -1408,14 +1436,18 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag)
 	if (len < PKT_ERR)
 		return;
 
+	bytes[snd_ch] += frame->Length;			// For AGW stats
+
+	if (AGWServ)
+		AGW_AX25_frame_analiz(snd_ch, TRUE, frame);
+
 	decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr);
 
 	if (is_excluded_call(snd_ch, path))
-		excluded =TRUE;
+		excluded = TRUE;
 
 	// if is_excluded_frm(snd_ch,f_id,data) then excluded:=TRUE;
 	
-
 	if (excluded)
 		return;
 
@@ -1448,7 +1480,20 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag)
 			AGW_Raw_monitor(snd_ch, frame);
 
 		if (KISSServ)
+		{
+			if (useKISSControls)
+			{
+				// Send a KISS Control frame with frame stats before data frame
+
+				int len = strlen(code);
+				UCHAR * Control = (UCHAR *)malloc(64);
+
+				len = sprintf(Control, "%c%cPKTINFO [%s]%c", FEND, (snd_ch) << 4 | QTSMKISSCMD, code, FEND);
+
+				KISSSendtoServer(NULL, Control, len);
+			}
 			KISS_on_data_out(snd_ch, frame, 0);
+		}
 	}
 
 	// Digipeat frame
@@ -1618,9 +1663,6 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag)
 		on_FRMR(socket, AX25Sess, path);
 		break;
 	}
-	
-	if (AGWServ)
-		AGW_AX25_frame_analiz(snd_ch, TRUE, frame);
 }
 
 
diff --git a/ax25_mod.c b/ax25_mod.c
index c8b9eef..dd84dda 100644
--- a/ax25_mod.c
+++ b/ax25_mod.c
@@ -25,6 +25,17 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 
 // I assume this modulates (and sends?} frames
 
+void InitBuffers();
+void EncodeRS(Byte * xData, Byte * xEncoded);
+void scrambler(UCHAR * in_buf, int Len);
+void fx25_encode_rs(Byte * data, Byte *parity, int pad, int rs_size);
+int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size);
+int il2p_get_new_bit_tail(UCHAR snd_ch, UCHAR bit);
+int il2p_get_new_bit(int snd_ch, Byte bit);
+int ARDOPSendToCard(int Chan, int Len);
+void Flush();
+void SampleSink(int LR, short Sample);
+
 int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen);
 
 //unit ax25_mod;
diff --git a/calibrateDialog.ui b/calibrateDialog.ui
index 66ef990..e786a23 100644
--- a/calibrateDialog.ui
+++ b/calibrateDialog.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>270</width>
-    <height>411</height>
+    <height>453</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -276,6 +276,19 @@
     </property>
    </widget>
   </widget>
+  <widget class="QPushButton" name="Cal1500">
+   <property name="geometry">
+    <rect>
+     <x>66</x>
+     <y>416</y>
+     <width>139</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>10 secs 1500Hz Tone</string>
+   </property>
+  </widget>
  </widget>
  <resources/>
  <connections/>
diff --git a/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h
index 62e1baa..b90fa38 100644
--- a/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h
+++ b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h
@@ -1,12 +1,12 @@
-#define _MSC_EXTENSIONS 
-#define _INTEGRAL_MAX_BITS 64
-#define _MSC_VER 1916
-#define _MSC_FULL_VER 191627051
-#define _MSC_BUILD 0
-#define _WIN32 
-#define _M_IX86 600
-#define _M_IX86_FP 2
-#define _CPPRTTI 
-#define _DEBUG 
-#define _MT 
-#define _DLL 
+#define _MSC_EXTENSIONS 
+#define _INTEGRAL_MAX_BITS 64
+#define _MSC_VER 1916
+#define _MSC_FULL_VER 191627051
+#define _MSC_BUILD 0
+#define _WIN32 
+#define _M_IX86 600
+#define _M_IX86_FP 2
+#define _CPPRTTI 
+#define _DEBUG 
+#define _MT 
+#define _DLL 
diff --git a/debug/moc_predefs-notpi4-64.h b/debug/moc_predefs-notpi4-64.h
index 6d708a1..8247e15 100644
--- a/debug/moc_predefs-notpi4-64.h
+++ b/debug/moc_predefs-notpi4-64.h
@@ -1,12 +1,12 @@
-#define _MSC_EXTENSIONS 
-#define _INTEGRAL_MAX_BITS 64
-#define _MSC_VER 1916
-#define _MSC_FULL_VER 191627043
-#define _MSC_BUILD 0
-#define _WIN32 
-#define _M_IX86 600
-#define _M_IX86_FP 2
-#define _CPPRTTI 
-#define _DEBUG 
-#define _MT 
-#define _DLL 
+#define _MSC_EXTENSIONS 
+#define _INTEGRAL_MAX_BITS 64
+#define _MSC_VER 1916
+#define _MSC_FULL_VER 191627043
+#define _MSC_BUILD 0
+#define _WIN32 
+#define _M_IX86 600
+#define _M_IX86_FP 2
+#define _CPPRTTI 
+#define _DEBUG 
+#define _MT 
+#define _DLL 
diff --git a/debug/moc_predefs-skigdebian.h b/debug/moc_predefs-skigdebian.h
index 6d708a1..8247e15 100644
--- a/debug/moc_predefs-skigdebian.h
+++ b/debug/moc_predefs-skigdebian.h
@@ -1,12 +1,12 @@
-#define _MSC_EXTENSIONS 
-#define _INTEGRAL_MAX_BITS 64
-#define _MSC_VER 1916
-#define _MSC_FULL_VER 191627043
-#define _MSC_BUILD 0
-#define _WIN32 
-#define _M_IX86 600
-#define _M_IX86_FP 2
-#define _CPPRTTI 
-#define _DEBUG 
-#define _MT 
-#define _DLL 
+#define _MSC_EXTENSIONS 
+#define _INTEGRAL_MAX_BITS 64
+#define _MSC_VER 1916
+#define _MSC_FULL_VER 191627043
+#define _MSC_BUILD 0
+#define _WIN32 
+#define _M_IX86 600
+#define _M_IX86_FP 2
+#define _CPPRTTI 
+#define _DEBUG 
+#define _MT 
+#define _DLL 
diff --git a/debug/moc_predefs.h b/debug/moc_predefs.h
index b857b2e..8247e15 100644
--- a/debug/moc_predefs.h
+++ b/debug/moc_predefs.h
@@ -1,7 +1,7 @@
 #define _MSC_EXTENSIONS 
 #define _INTEGRAL_MAX_BITS 64
 #define _MSC_VER 1916
-#define _MSC_FULL_VER 191627045
+#define _MSC_FULL_VER 191627043
 #define _MSC_BUILD 0
 #define _WIN32 
 #define _M_IX86 600
diff --git a/devicesDialog.ui b/devicesDialog.ui
index 9fef8ab..40bad5e 100644
--- a/devicesDialog.ui
+++ b/devicesDialog.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>535</width>
-    <height>698</height>
+    <width>528</width>
+    <height>721</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -20,9 +20,9 @@
    <property name="geometry">
     <rect>
      <x>5</x>
-     <y>10</y>
-     <width>512</width>
-     <height>671</height>
+     <y>0</y>
+     <width>513</width>
+     <height>711</height>
     </rect>
    </property>
    <property name="verticalScrollBarPolicy">
@@ -40,7 +40,7 @@
       <x>0</x>
       <y>0</y>
       <width>508</width>
-      <height>668</height>
+      <height>706</height>
      </rect>
     </property>
     <widget class="QGroupBox" name="groupBox">
@@ -49,7 +49,7 @@
        <x>5</x>
        <y>10</y>
        <width>541</width>
-       <height>331</height>
+       <height>361</height>
       </rect>
      </property>
      <property name="title">
@@ -582,14 +582,27 @@
        <string>Only Mix/Snoop Devices (needs custom .asoundrc)</string>
       </property>
      </widget>
+     <widget class="QCheckBox" name="useKISSControls">
+      <property name="geometry">
+       <rect>
+        <x>20</x>
+        <y>328</y>
+        <width>271</width>
+        <height>17</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Enable KISS Control and Reporting</string>
+      </property>
+     </widget>
     </widget>
-    <widget class="QGroupBox" name="groupBox_2">
+    <widget class="QGroupBox" name="Goup">
      <property name="geometry">
       <rect>
        <x>10</x>
-       <y>360</y>
+       <y>380</y>
        <width>481</width>
-       <height>101</height>
+       <height>133</height>
       </rect>
      </property>
      <property name="title">
@@ -600,7 +613,7 @@
        <rect>
         <x>22</x>
         <y>24</y>
-        <width>121</width>
+        <width>129</width>
         <height>18</height>
        </rect>
       </property>
@@ -633,7 +646,7 @@
        <rect>
         <x>22</x>
         <y>48</y>
-        <width>111</width>
+        <width>131</width>
         <height>18</height>
        </rect>
       </property>
@@ -718,7 +731,7 @@
        <rect>
         <x>20</x>
         <y>74</y>
-        <width>111</width>
+        <width>135</width>
         <height>18</height>
        </rect>
       </property>
@@ -726,12 +739,97 @@
        <string>UDP Sound Server Host</string>
       </property>
      </widget>
+     <widget class="QComboBox" name="SixPackSerial">
+      <property name="geometry">
+       <rect>
+        <x>160</x>
+        <y>100</y>
+        <width>81</width>
+        <height>20</height>
+       </rect>
+      </property>
+      <property name="editable">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_17">
+      <property name="geometry">
+       <rect>
+        <x>20</x>
+        <y>98</y>
+        <width>111</width>
+        <height>18</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>6Pack Serial Port</string>
+      </property>
+     </widget>
+     <widget class="QLineEdit" name="SixPackTCP">
+      <property name="geometry">
+       <rect>
+        <x>320</x>
+        <y>100</y>
+        <width>47</width>
+        <height>20</height>
+       </rect>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_7">
+      <property name="geometry">
+       <rect>
+        <x>252</x>
+        <y>101</y>
+        <width>63</width>
+        <height>18</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>TCP Port</string>
+      </property>
+     </widget>
+     <widget class="QCheckBox" name="SixPackEnable">
+      <property name="geometry">
+       <rect>
+        <x>380</x>
+        <y>99</y>
+        <width>70</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Enabled</string>
+      </property>
+     </widget>
+     <widget class="QLineEdit" name="MgmtPort">
+      <property name="geometry">
+       <rect>
+        <x>410</x>
+        <y>25</y>
+        <width>47</width>
+        <height>20</height>
+       </rect>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_18">
+      <property name="geometry">
+       <rect>
+        <x>340</x>
+        <y>25</y>
+        <width>91</width>
+        <height>20</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Mgmt Port</string>
+      </property>
+     </widget>
     </widget>
     <widget class="QGroupBox" name="groupBox_3">
      <property name="geometry">
       <rect>
        <x>10</x>
-       <y>470</y>
+       <y>520</y>
        <width>481</width>
        <height>151</height>
       </rect>
@@ -933,8 +1031,8 @@
     <widget class="QWidget" name="layoutWidget">
      <property name="geometry">
       <rect>
-       <x>140</x>
-       <y>626</y>
+       <x>130</x>
+       <y>670</y>
        <width>215</width>
        <height>33</height>
       </rect>
diff --git a/il2p.c b/il2p.c
index c28c85c..1b8e2b8 100644
--- a/il2p.c
+++ b/il2p.c
@@ -88,7 +88,7 @@ void Debugprintf(const char * format, ...);
 int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count);
 
 extern int openTraceLog();
-extern uint64_t writeTraceLog(char * Data, char Dirn);
+extern uint64_t writeTraceLog(char * Data);
 extern void closeTraceLog();
 
 #define MAX_ADEVS 3			
@@ -4839,7 +4839,7 @@ void debugHexDump(unsigned char * Data, int Len, char Dirn)
 			Line[Len * 3] = 10;
 			Line[Len * 3 + 1] = 0;
 		}
-		writeTraceLog(Line, Dirn);
+		writeTraceLog(Line);
 
 		Data += 16;
 		Len -= 16;
diff --git a/kiss_mode.c b/kiss_mode.c
index 56feb2a..6c638f1 100644
--- a/kiss_mode.c
+++ b/kiss_mode.c
@@ -22,6 +22,8 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 
 #include "UZ7HOStuff.h"
 
+int add_raw_frames(int snd_ch, string * frame, TStringList * buf);
+
 // I think I need a struct for each connection, but a simple array of entries should be fine
 // My normal ** and count system
 // Each needs an input buffer of max size kiss frame and length (or maybe string is a good idea)
@@ -35,6 +37,7 @@ int KISSConCount = 0;
 #define TFESC 0xDD
 #define KISS_ACKMODE 0x0C
 #define KISS_DATA 0
+#define QTSMKISSCMD 7
 
 struct TKISSMode_t  KISS;
 
@@ -75,7 +78,7 @@ end;
 
 void KISS_add_stream(void * Socket)
 {
-	// Add a new connection. Called wheKISSn QT accepts an incoming call}
+	// Add a new connection. Called when QT accepts an incoming call}
 
 	TKISSMode * KISS;
 
diff --git a/main.cpp b/main.cpp
index a3b08d4..63391fd 100644
--- a/main.cpp
+++ b/main.cpp
@@ -33,15 +33,18 @@ extern void saveSettings();
 extern int Closing;
 
 workerThread *t;
+serialThread *serial;
 mynet m1;
 
 QCoreApplication * a;		
 
 QtSoundModem * w;
 
+
 int main(int argc, char *argv[])
 {
 	char Title[128];
+	QString Response;
 
 	if (argc > 1 && strcmp(argv[1], "nogui") == 0)
 		nonGUIMode = 1;
@@ -78,6 +81,7 @@ int main(int argc, char *argv[])
 
 	QObject::connect(&m1, SIGNAL(HLSetPTT(int)), &m1, SLOT(doHLSetPTT(int)), Qt::QueuedConnection);
 	QObject::connect(&m1, SIGNAL(FLRigSetPTT(int)), &m1, SLOT(doFLRigSetPTT(int)), Qt::QueuedConnection);
+	QObject::connect(&m1, SIGNAL(mgmtSetPTT(int, int)), &m1, SLOT(domgmtSetPTT(int, int)), Qt::QueuedConnection);
 
 
 	QObject::connect(&m1, SIGNAL(startTimer(int)), &m1, SLOT(dostartTimer(int)), Qt::QueuedConnection);
diff --git a/sm_main.c.bak b/sm_main.c.bak
index b5b6499..e017275 100644
--- a/sm_main.c.bak
+++ b/sm_main.c.bak
@@ -1,1431 +1,1431 @@
-/*
-Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
-
-This file is part of QtSoundModem
-
-QtSoundModem is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-QtSoundModem is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-newsamp
-
-You should have received a copy of the GNU General Public License
-along with QtSoundModem.  If not, see http://www.gnu.org/licenses
-
-*/
-
-// UZ7HO Soundmodem Port by John Wiseman G8BPQ
-
-#include "UZ7HOStuff.h"
-
-void make_core_BPF(UCHAR snd_ch, short freq, short width);
-void make_core_TXBPF(UCHAR snd_ch, float freq, float width);
-void make_core_INTR(UCHAR snd_ch);
-void make_core_LPF(UCHAR snd_ch, short width);
-void dw9600ProcessSample(int snd_ch, short Sample);
-void init_RUH48(int snd_ch);
-void init_RUH96(int snd_ch);
-
-char modes_name[modes_count][21] = 
-{
-	"AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd",
-	"BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd",
-	"QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd",
-	"QPSK V26A 2400bps","8PSK V27 4800bps","QPSK V26B 2400bps", "Not Available",
-	"QPSK V26A 600bps", "8PSK 900bps", "RUH 4800(DW)", "RUH 9600(DW)"
-};
-
-typedef struct wavehdr_tag {
-	unsigned short *  lpData;   /* pointer to locked data buffer */
-	int dwBufferLength;         /* length of data buffer */
-	int dwBytesRecorded;        /* used for input only */
-	int * dwUser;               /* for client's use */
-	int dwFlags;                /* assorted flags (see defines) */
-	int dwLoops;                /* loop control counter */
-	struct wavehdr_tag *lpNext; /* reserved for driver */
-	int *   reserved;           /* reserved for driver */
-} WAVEHDR, *PWAVEHDR,  * NPWAVEHDR,  * LPWAVEHDR;
-
-extern int pnt_change[5];
-int debugmode = 0;
-extern float src_buf[5][2048];
-extern Byte RCVR[5];
-
-int SatelliteMode = 0;
-
-int UDPServerPort = 8884;
-int UDPClientPort = 8888;
-int TXPort = 8884;
-
-BOOL Firstwaterfall = 1;
-BOOL Secondwaterfall = 1;
-int multiCore = FALSE;
-
-BOOL MinOnStart  =  0;
-//RS TReedSolomon;
-//  Form1 TForm1;
-//  WaveFormat TWaveFormatEx;
-int Channels = 2;
-int BitsPerSample = 16;
-float TX_Samplerate = 12000;
-float RX_Samplerate = 12000;
-int RX_SR = 11025;
-int TX_SR = 11025;
-int RX_PPM = 0;
-int TX_PPM = 0;
-int tx_bufsize = 512;
-int rx_bufsize = 512;
-int tx_bufcount = 16;
-int rx_bufcount = 16;
-int  mouse_down[2] = {0, 0};
-//UCHAR * RX_pBuf array[257];
-//  RX_header array[1..256] of TWaveHdr;
-//  TX_pBuf array[1..4,1..256] of pointer;
-//TX_header array[1..4,1..256] of TWaveHdr;
-UCHAR calib_mode[5] = {0,0,0,0};
-UCHAR snd_status [5] = {0,0,0,0};
-UCHAR buf_status [5]  = {0,0,0,0};
-UCHAR tx_buf_num1 [5] = {0,0,0,0};
-UCHAR tx_buf_num [5] = {0,0,0,0};
-
-extern short active_rx_freq[5];
-
-unsigned int pskStates[4] = {0, 0, 0, 0};	
-
-int speed[5] = {0,0,0,0};
-int panels[6] = {1,1,1,1,1};
-
-short fft_buf[2][8192];
-UCHAR fft_disp[2][1024];
-int fftCount = 0;			// FTF samples collected
-
-//  bm array[1..4] of TBitMap;
-//  bm1,bm2,bm3 TBitMap;
-
-//  WaveInHandle hWaveIn;
-//  WaveOutHandle array[1..4] of hWaveOut;
-int RXBufferLength;
-
-int grid_time = 0;
-int fft_mult = 0;
-int fft_spd = 3;
-int grid_timer = 0;
-int stop_wf  = 0;
-int raduga = DISP_RGB;
-char snd_rx_device_name[32] = "";
-char snd_tx_device_name[32] = "";
-int snd_rx_device = 0;
-int snd_tx_device = 0;
-UCHAR mod_icon_status = MOD_IDLE;
-UCHAR last_mod_icon_status = MOD_IDLE;
-UCHAR icon_timer = 0;
-//  TelIni TIniFile;
-char cur_dir[] = "";
-//  TimerId1 cardinal;
-//  TimerId2 cardinal;
-UCHAR TimerStat1 = TIMER_FREE;
-UCHAR TimerStat2 = TIMER_FREE;
-int stat_log = FALSE;
-
-int PTT_device = FALSE;
-int RX_device = FALSE;
-int TX_device = FALSE;
-int TX_rotate = FALSE;
-int UsingBothChannels = FALSE;
-int UsingLeft = FALSE;
-int UsingRight = FALSE;
-
-int SCO = FALSE;
-int DualPTT = TRUE;
-UCHAR  DebugMode = 0;
-UCHAR TimerEvent = TIMER_EVENT_OFF;
-int nr_monitor_lines = 50;
-int UTC_Time = FALSE;
-int MainPriority = 0;
-//  MainThreadHandle THandle;
-UCHAR w_state = WIN_MAXIMIZED;
- 
- 
-void get_filter_values(UCHAR snd_ch)
-{
-	//, unsigned short dbpf,
-//unsigned short dtxbpf,
-//unsigned short dbpftap,
-//unsigned short dlpf, 
-//unsigned short dlpftap)
-//	speed[snd_ch], bpf[snd_ch], txbpf[snd_ch], bpf_tap[snd_ch], lpf[snd_ch], lpf_tap[snd_ch]);
-
-	switch (speed[snd_ch])
-	{
-	case SPEED_8P4800:
-
-		lpf[snd_ch] = MODEM_8P4800_LPF;
-		bpf[snd_ch] = MODEM_8P4800_BPF;
-		txbpf[snd_ch] = MODEM_8P4800_TXBPF;
-		BPF_tap[snd_ch] = MODEM_8P4800_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_8P4800_LPF_TAP;
-		break;
-
-	case SPEED_MP400:
-
-		lpf[snd_ch] = MODEM_MP400_LPF;
-		bpf[snd_ch] = MODEM_MP400_BPF;
-		txbpf[snd_ch] = MODEM_MP400_TXBPF;
-		BPF_tap[snd_ch] = MODEM_MP400_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_MP400_LPF_TAP;
-
-		break;
-
-
-	case SPEED_Q4800:
-
-		lpf[snd_ch] = MODEM_Q4800_LPF;
-		bpf[snd_ch] = MODEM_Q4800_BPF;
-		txbpf[snd_ch] = MODEM_Q4800_TXBPF;
-		BPF_tap[snd_ch] = MODEM_Q4800_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_Q4800_LPF_TAP;
-
-		break;
-
-	case SPEED_Q3600:
-
-		lpf[snd_ch] = MODEM_Q3600_LPF;
-		bpf[snd_ch] = MODEM_Q3600_BPF;
-		txbpf[snd_ch] = MODEM_Q3600_TXBPF;
-		BPF_tap[snd_ch] = MODEM_Q3600_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_Q3600_LPF_TAP;
-		break;
-
-	case SPEED_Q2400:
-
-		lpf[snd_ch] = MODEM_Q2400_LPF;
-		bpf[snd_ch] = MODEM_Q2400_BPF;
-		txbpf[snd_ch] = MODEM_Q2400_TXBPF;
-		BPF_tap[snd_ch] = MODEM_Q2400_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_Q2400_LPF_TAP;
-
-		break;
-
-	case SPEED_DW2400:
-	case SPEED_2400V26B:
-
-
-		lpf[snd_ch] = MODEM_DW2400_LPF;
-		bpf[snd_ch] = MODEM_DW2400_BPF;
-		txbpf[snd_ch] = MODEM_DW2400_TXBPF;
-		BPF_tap[snd_ch] = MODEM_DW2400_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_DW2400_LPF_TAP;
-		break;
-
-	case SPEED_P2400:
-
-		lpf[snd_ch] = MODEM_P2400_LPF;
-		bpf[snd_ch] = MODEM_P2400_BPF;
-		txbpf[snd_ch] = MODEM_P2400_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P2400_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P2400_LPF_TAP;
-		break;
-
-	case SPEED_P1200:
-
-		lpf[snd_ch] = MODEM_P1200_LPF;
-		bpf[snd_ch] = MODEM_P1200_BPF;
-		txbpf[snd_ch] = MODEM_P1200_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP;
-		break;
-
-	case SPEED_P600:
-
-		lpf[snd_ch] = MODEM_P600_LPF;
-		bpf[snd_ch] = MODEM_P600_BPF;
-		txbpf[snd_ch] = MODEM_P600_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P600_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P600_LPF_TAP;
-		break;
-
-	case SPEED_P300:
-
-		lpf[snd_ch] = MODEM_P300_LPF;
-		bpf[snd_ch] = MODEM_P300_BPF;
-		txbpf[snd_ch] = MODEM_P300_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P300_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P300_LPF_TAP;
-		break;
-
-	case SPEED_300:
-
-		lpf[snd_ch] = MODEM_300_LPF;
-		bpf[snd_ch] = MODEM_300_BPF;
-		txbpf[snd_ch] = MODEM_300_TXBPF;
-		BPF_tap[snd_ch] = MODEM_300_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_300_LPF_TAP;
-
-		break;
-
-	case SPEED_600:
-
-		lpf[snd_ch] = MODEM_600_LPF;
-		bpf[snd_ch] = MODEM_600_BPF;
-		txbpf[snd_ch] = MODEM_600_TXBPF;
-		BPF_tap[snd_ch] = MODEM_600_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_600_LPF_TAP;
-
-		break;
-
-	case SPEED_1200:
-
-		lpf[snd_ch] = MODEM_1200_LPF;
-		bpf[snd_ch] = MODEM_1200_BPF;
-		txbpf[snd_ch] = MODEM_1200_TXBPF;
-		BPF_tap[snd_ch] = MODEM_1200_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_1200_LPF_TAP;
-		break;
-
-	case SPEED_2400:
-
-		lpf[snd_ch] = MODEM_2400_LPF;
-		bpf[snd_ch] = MODEM_2400_BPF;
-		txbpf[snd_ch] = MODEM_2400_TXBPF;
-		BPF_tap[snd_ch] = MODEM_2400_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_2400_LPF_TAP;
-		break;
-
-
-	case SPEED_Q300:
-	case SPEED_8PSK300:
-
-		lpf[snd_ch] = MODEM_P300_LPF;
-		bpf[snd_ch] = MODEM_P300_BPF;
-		txbpf[snd_ch] = MODEM_P300_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P300_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P300_LPF_TAP;
-
-		break;
-
-/*
-
-	case SPEED_Q1200:
-
-		lpf[snd_ch] = MODEM_P1200_LPF;
-		bpf[snd_ch] = MODEM_P1200_BPF;
-		txbpf[snd_ch] = MODEM_P1200_TXBPF;
-		BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP;
-		LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP;
-		break;
-*/
-	}
-}
-
-
-void init_2400(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_FSK;
-	rx_shift[snd_ch] = 1805;
-	rx_baudrate[snd_ch] = 2400;
-	tx_bitrate[snd_ch] = 2400;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_1200(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_FSK;
-	rx_shift[snd_ch] = 1000;
-
-	if (stdtones)
-		rx_freq[snd_ch] = 1700;
-
-	rx_baudrate[snd_ch] = 1200;
-	tx_bitrate[snd_ch] = 1200;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_600(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_FSK;
-	rx_shift[snd_ch] = 450;
-
-	rx_baudrate[snd_ch] = 600;
-	tx_bitrate[snd_ch] = 600;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_300(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_FSK;
-	rx_shift[snd_ch] = 200;
-	rx_baudrate[snd_ch] = 300;
-	tx_bitrate[snd_ch] = 300;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_MP400(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_MPSK;
-	rx_shift[snd_ch] = 175 /*sbc*/ * 3;
-	rx_baudrate[snd_ch] = 100;
-	tx_bitrate[snd_ch] = 400;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-
-void init_8P4800(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_8PSK;
-	if (stdtones)
-		rx_freq[snd_ch] = 1800;
-
-	rx_shift[snd_ch] = 1600;
-	rx_baudrate[snd_ch] = 1600;
-	tx_bitrate[snd_ch] = 4800;
-	pskStates[snd_ch] = 8;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_V26B2400(int snd_ch)
-{
-	qpsk_set[snd_ch].mode = QPSK_V26;
-	modem_mode[snd_ch] = MODE_PI4QPSK;
-
-	if (stdtones)
-		rx_freq[snd_ch] = 1800;
-
-	rx_shift[snd_ch] = 1200;
-	rx_baudrate[snd_ch] = 1200;
-	tx_bitrate[snd_ch] = 2400;
-	pskStates[snd_ch] = 8;			// Pretend 8 so quality calc works
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_DW2400(int snd_ch)
-{
-	qpsk_set[snd_ch].mode = QPSK_V26;
-	modem_mode[snd_ch] = MODE_QPSK;
-
-	if (stdtones)
-		rx_freq[snd_ch] = 1800;
-
-	rx_shift[snd_ch] = 1200;
-	rx_baudrate[snd_ch] = 1200;
-	tx_bitrate[snd_ch] = 2400;
-	pskStates[snd_ch] = 4;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_Q4800(int snd_ch)
-{
-	qpsk_set[snd_ch].mode = QPSK_SM;
-	modem_mode[snd_ch] = MODE_QPSK;
-	rx_shift[snd_ch] = 2400;
-	rx_baudrate[snd_ch] = 2400;
-	tx_bitrate[snd_ch] = 4800;
-	pskStates[snd_ch] = 4;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_Q3600(int snd_ch)
-{
-	qpsk_set[snd_ch].mode = QPSK_SM;
-	modem_mode[snd_ch] = MODE_QPSK;
-	rx_shift[snd_ch] = 1800;
-	rx_baudrate[snd_ch] = 1800;
-	tx_bitrate[snd_ch] = 3600;
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_Q2400(int snd_ch)
-{
-  qpsk_set[snd_ch].mode = QPSK_SM;
-  modem_mode[snd_ch] = MODE_QPSK;
-  rx_shift[snd_ch] = 1200;
-  rx_baudrate[snd_ch] = 1200;
-  tx_bitrate[snd_ch] = 2400;
-  pskStates[snd_ch] = 4;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_P2400(int snd_ch)
-{
-  modem_mode[snd_ch] = MODE_BPSK;
-  rx_shift[snd_ch] = 2400;
-  rx_baudrate[snd_ch] = 2400;
-  tx_bitrate[snd_ch] = 2400;
-  pskStates[snd_ch] = 2;
-  
-  if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_P1200(int snd_ch)
-{
-  modem_mode[snd_ch] = MODE_BPSK;
-  rx_shift[snd_ch] = 1200;
-  rx_baudrate[snd_ch] = 1200;
-  tx_bitrate[snd_ch] = 1200;
-  pskStates[snd_ch] = 2;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_P600(int snd_ch)
-{
-  modem_mode[snd_ch] = MODE_BPSK;
-  rx_shift[snd_ch] = 600;
-  rx_baudrate[snd_ch] = 600;
-  tx_bitrate[snd_ch] = 600;
-  pskStates[snd_ch] = 2;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_P300(int snd_ch)
-{
-  modem_mode[snd_ch] = MODE_BPSK;
-  rx_shift[snd_ch] = 300;
-  rx_baudrate[snd_ch] = 300;
-  pskStates[snd_ch] = 2;
-  tx_bitrate[snd_ch] = 300;
-
-  if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_Q300(int snd_ch)
-{
-	qpsk_set[snd_ch].mode = QPSK_V26;
-	modem_mode[snd_ch] = MODE_QPSK;
-
-	rx_shift[snd_ch] = 300;
-	rx_baudrate[snd_ch] = 300;
-	tx_bitrate[snd_ch] = 600;
-	pskStates[snd_ch] = 4;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_8PSK300(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_8PSK;
-
-	rx_shift[snd_ch] = 300;
-	rx_baudrate[snd_ch] = 300;
-	tx_bitrate[snd_ch] = 900;
-	pskStates[snd_ch] = 8;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-void init_ARDOP(int snd_ch)
-{
-	modem_mode[snd_ch] = MODE_ARDOP;
-	rx_shift[snd_ch] = 500;
-	rx_freq[snd_ch] = 1500;
-	rx_baudrate[snd_ch] = 500;
-	tx_bitrate[snd_ch] = 500;
-
-	if (modem_def[snd_ch])
-		get_filter_values(snd_ch);
-}
-
-
-void init_speed(int snd_ch);
-
-void set_speed(int snd_ch, int Modem)
-{
-	speed[snd_ch] = Modem;
-
-	init_speed(snd_ch);
-
-}
-
-int needPSKRefresh = 0;
-
-void init_speed(int snd_ch)
-{
-	int low, high;
-
-	pskStates[snd_ch] = 0;		// Not PSK
-
-	switch (speed[snd_ch])
-	{
-	case SPEED_8P4800:
-		init_8P4800(snd_ch);
-		break;
-
-	case SPEED_2400V26B:
-		init_V26B2400(snd_ch);
-
-		break;
-
-	case SPEED_DW2400:
-		init_DW2400(snd_ch);
-		break;
-
-	case SPEED_MP400:
-		init_MP400(snd_ch);
-		break;
-	case SPEED_Q4800:
-		init_Q4800(snd_ch);
-		break;
-
-	case SPEED_Q3600:
-		init_Q3600(snd_ch);
-		break;
-
-	case SPEED_Q2400:
-		init_Q2400(snd_ch);
-		break;
-
-	case SPEED_P2400:
-		init_P2400(snd_ch);
-		break;
-
-	case SPEED_P1200:
-		init_P1200(snd_ch);
-		break;
-
-//	case SPEED_Q1200:
-//		init_Q1200(snd_ch);
-//		break;
-
-	case SPEED_P600:
-		init_P600(snd_ch);
-		break;
-
-	case SPEED_P300:
-		init_P300(snd_ch);
-		break;
-
-	case SPEED_Q300:
-		init_Q300(snd_ch);
-		break;
-
-	case SPEED_8PSK300:
-		init_8PSK300(snd_ch);
-		break;
-
-	case SPEED_300:
-
-		init_300(snd_ch);
-		break;
-
-	case SPEED_600:
-
-		init_600(snd_ch);
-		break;
-
-	case SPEED_1200:
-
-		init_1200(snd_ch);
-		break;
-
-	case SPEED_2400:
-
-		init_2400(snd_ch);
-		break;
-
-	case SPEED_ARDOP:
-
-		init_ARDOP(snd_ch);
-		break;
-
-	case SPEED_RUH48:
-
-		init_RUH48(snd_ch);
-		break;
-
-	case SPEED_RUH96:
-
-		init_RUH96(snd_ch);
-		break;
-
-	}
-
-	//QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end;
-	//QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end;
-
-	if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK)
-	{
-		switch (qpsk_set[snd_ch].mode)
-		{
-		case QPSK_SM:
-
-			memcpy(&qpsk_set[snd_ch].tx[0], "\0\1\2\3", 4);
-			memcpy(&qpsk_set[snd_ch].rx[0], "\x0\x20\x40\x60", 4);
-			break;
-
-		case QPSK_V26:
-
-			memcpy(&qpsk_set[snd_ch].tx[0], "\2\3\1\0", 4);
-			memcpy(&qpsk_set[snd_ch].rx[0], "\x60\x40\0\x20", 4);
-			break;
-		}
-	}
-
-	tx_shift[snd_ch] = rx_shift[snd_ch];
-	tx_baudrate[snd_ch] = rx_baudrate[snd_ch];
-	low = roundf(rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch] + 1);
-	high = roundf(RX_Samplerate / 2 - (rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch]));
-
-	if (rx_freq[snd_ch] - low < 0)  rx_freq[snd_ch] = low;
-	if (high - rx_freq[snd_ch] < 0) rx_freq[snd_ch] = high;
-
-	tx_freq[snd_ch] = rx_freq[snd_ch];
-
-	make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
-	make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]);
-	make_core_INTR(snd_ch);
-	make_core_LPF(snd_ch, lpf[snd_ch]);
-
-	/*
-	  for i = 0 to 16 do
-		for j = 0 to nr_emph do with DET[j,i] do
-		begin
-		  minamp[snd_ch] = 0;
-		  maxamp[snd_ch] = 0;
-		  ones[snd_ch] = 0;
-		  zeros[snd_ch] = 0;
-		  sample_cnt[snd_ch] = 0;
-		  bit_cnt[snd_ch] = 0;
-		  bit_osc[snd_ch] = 0;
-		  frame_status[snd_ch] = FRAME_WAIT;
-		end;
-	  form1.show_modes;
-	  form1.show_freq_a;
-	  form1.show_freq_b;
-	  */
-	NeedWaterfallHeaders = TRUE;
-
-	CheckPSKWindows();
-}
-
-
-void  chk_snd_buf(float * buf, int len)
-{
-	word i;
-	boolean  good;
-	single prev_amp;
-
-	if (len < 2)
-		return;
-
-	good = FALSE;
-	i = 1;
-	prev_amp = buf[0];
-	do
-	{
-		if (buf[i++] != prev_amp)
-			good = TRUE;
-
-	} while (good == FALSE && i < len);
-
-	// Make noise
-	if (!good)
-		for (i = 0; i < len; i++)
-			buf[i] = rand();
-}
-
-#ifdef WIN32
-
-typedef void *HANDLE;
-typedef unsigned long DWORD;
-
-#define WINAPI __stdcall
-__declspec(dllimport)
-DWORD
-WINAPI
-WaitForSingleObject(
-	__in HANDLE hHandle,
-	__in DWORD dwMilliseconds
-);
-
-
-
-
-#define pthread_t uintptr_t
-
-uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist);
-#else
-
-#include <pthread.h>
-
-pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist)
-{
-	pthread_t thread;
-
-	if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0)
-		perror("New Thread");
-
-	return thread;
-}
-
-#endif
-
-void runModemthread(void * param);
-
-void runModems()
-{
-	int snd_ch, res;
-	pthread_t thread[4] = { 0,0,0,0 };
-
-	for (snd_ch = 0; snd_ch < 4; snd_ch++)
-	{
-		if (soundChannel[snd_ch] == 0)				// Unused channed
-			continue;	
-	
-		if (modem_mode[snd_ch] == MODE_ARDOP)
-			continue;			// Processed above
-
-		if (modem_mode[snd_ch] == MODE_RUH)
-			continue;			// Processed above
-
-		// do we need to do this again ??
-		// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
-
-		if (multiCore)			// Run modems in separate threads
-			thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch);
-		else
-			runModemthread((void *)(size_t)snd_ch);
-	}
-
-	if (multiCore)
-	{
-#ifdef WIN32
-		if (thread[0]) WaitForSingleObject(&thread[0], 2000);
-		if (thread[1]) WaitForSingleObject(&thread[1], 2000);
-		if (thread[2]) WaitForSingleObject(&thread[2], 2000);
-		if (thread[3]) WaitForSingleObject(&thread[3], 2000);
-#else
-		if (thread[0]) pthread_join(thread[0], &res);
-		if (thread[1]) pthread_join(thread[1], &res);
-		if (thread[2]) pthread_join(thread[2], &res);
-		if (thread[3]) pthread_join(thread[3], &res);
-#endif
-	}
-}
-
-Byte rcvr_idx;
-
-void runModemthread(void * param)
-{
-	int snd_ch = (int)(size_t)param;
-
-	// I want to run lowest to highest to simplify my display 
-
-	int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest
-	int lastrx = RCVR[snd_ch] * 2;
-
-	if (soundChannel[snd_ch] == 0)				// Unused channed
-		return;
-
-	for (rcvr_idx = 0; rcvr_idx <= lastrx; rcvr_idx++)
-	{
-		active_rx_freq[snd_ch] = rxOffset + chanOffset[snd_ch] + rx_freq[snd_ch] + offset;
-		offset += rcvr_offset[snd_ch];
-
-		Demodulator(snd_ch, rcvr_idx, src_buf[modemtoSoundLR[snd_ch]], rcvr_idx == lastrx, offset == 0);
-	}
-}
-
-// I think this processes a buffer of samples
-
-int Toggle = 0;
-
-void BufferFull(short * Samples, int nSamples)			// These are Stereo Samples
-{
-	word i, i1, j;
-	Byte snd_ch, rcvr_idx;
-	int buf_offset;
-	int Needed;
-	short * data1;
-	short * data2 = 0;
-
-	// if UDP server active send as UDP Datagram
-
-	if (UDPServ)	// Extract just left
-	{
-		short Buff[1024];
-
-		i1 = 0;
-
-		for (i = 0; i < rx_bufsize; i++)
-		{
-			Buff[i] = Samples[i1];
-			i1 += 2;
-		}
-
-		sendSamplestoUDP(Buff, 512, TXPort);
-	}
-
-	// Do RSID processing (can we also use this for waterfall??
-
-	RSIDProcessSamples(Samples, nSamples);
-
-	// Do FFT on every 4th buffer (2048 samples)
-
-	// if in Satellite Mode look for a Tuning signal
-
-//	if (SatelliteMode)
-//	{
-//		doTuning(Samples, nSamples);
-//	}
-
-	for (snd_ch = 0; snd_ch < 4; snd_ch++)
-	{
-		if (soundChannel[snd_ch] == 0)				// Unused channed
-			continue;
-
-		if (pnt_change[snd_ch])
-		{
-			make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
-			make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]);
-			pnt_change[snd_ch] = FALSE;
-		}
-
-	}
-
-	// I don't think we should process RX if either is sending
-
-	if (snd_status[0] != SND_TX && snd_status[1] != SND_TX && snd_status[2] != SND_TX && snd_status[3] != SND_TX)
-	{
-		for (snd_ch = 0; snd_ch < 4; snd_ch++)
-		{
-			if (soundChannel[snd_ch] == 0)				// Unused channed
-				continue;
-
-			if (modem_mode[snd_ch] == MODE_ARDOP)
-			{
-				short ardopbuff[1200];
-				i1 = 0;
-
-				if (using48000)
-				{
-					i1 = 0;
-					j = 0;
-
-					//	Need to downsample 48K to 12K
-					//	Try just skipping 3 samples	
-
-					nSamples /= 4;
-
-					for (i = 0; i < nSamples; i++)
-					{
-						ardopbuff[i] = Samples[i1];		
-						i1 += 8;
-					}
-				}
-				else
-				{
-					for (i = 0; i < nSamples; i++)
-					{
-						ardopbuff[i] = Samples[i1];
-						i1++;
-						i1++;
-					}
-				}
-				ARDOPProcessNewSamples(snd_ch, ardopbuff, nSamples);
-			}
-
-			else if (modem_mode[snd_ch] == MODE_RUH)
-			{
-				i1 = 0;
-				if (modemtoSoundLR[snd_ch] == 1)		// Using Right Chan
-					i1++;			
-
-				for (i = 0; i < nSamples; i++)
-				{
-					dw9600ProcessSample(snd_ch, Samples[i1]);
-					i1++;
-					i1++;
-				}
-			}
-			ProcessRXFrames(snd_ch);
-		}
-
-		// extract mono samples from data. 
-
-		data1 = Samples;
-
-		if (using48000)
-		{
-			i1 = 0;
-			j = 0;
-
-			//	Need to downsample 48K to 12K
-			//	Try just skipping 3 samples	
-
-			nSamples /= 4;
-
-			for (i = 0; i < nSamples; i++)
-			{
-				Samples[j++] = Samples[i1];
-				Samples[j++] = Samples[i1 + 1];
-				i1 += 8;
-			}
-		}
-
-		i1 = 0;
-
-		// src_buf[0] is left data,. src_buf[1] right
-
-		// We could skip extracting other channel if only one in use - is it worth it??
-
-		if (UsingBothChannels)
-		{
-			for (i = 0; i < nSamples; i++)
-			{
-				src_buf[0][i] = data1[i1];
-				i1++;
-				src_buf[1][i] = data1[i1];
-				i1++;
-			}
-		}
-		else if (UsingRight)
-		{
-			// Extract just right
-
-			i1 = 1;
-
-			for (i = 0; i < nSamples; i++)
-			{
-				src_buf[1][i] = data1[i1];
-				i1 += 2;
-			}
-		}
-		else
-		{
-			// Extract just left
-
-			for (i = 0; i < nSamples; i++)
-			{
-				src_buf[0][i] = data1[i1];
-				i1 += 2;
-			}
-		}
-
-		// Run modems before waterfall so fft buffer has values from before sync was detected
-
-		runModems();
-
-		// Do whichever waterfall is needed
-
-		// We need to run the waterfall FFT for the frequency guessing to work
-
-		// testing
-
-
-
-		int FirstWaterfallChan = 0;
-
-		doWaterfall(FirstWaterfallChan);
-		if (Firstwaterfall || Secondwaterfall)
-			displayWaterfall();
-
-
-
-
-/*
-
-		short * ptr1 = &fft_buf[0][fftCount];
-		short * ptr2 = &fft_buf[1][fftCount];
-
-		int remainingSamples = rx_bufsize;
-
-		if (UsingLeft == 0)
-		{
-			FirstWaterfallChan = 1;
-			data1++;					// to Right Samples
-		}
-
-		if (UsingBothChannels)			// Second is always Right
-			data2 = &Samples[1];		// to Right Samples
-
-
-		// FFT size isn't necessarily a multiple of rx_bufsize, so this is a bit more complicated
-		// Save FFTSize samples then process. Put the unused bits back in the fft buffer
-
-		// Collect samples for both channels if needed
-
-		Toggle++;
-
-		Needed = FFTSize - fftCount;
-
-		if (Needed <= rx_bufsize)
-		{
-			// add Needed samples to fft_buf and process. Copy rest to start of fft_buf
-
-			for (i = 0; i < Needed; i++)
-			{
-				*ptr1++ = *data1;
-				data1 += 2;
-			}
-
-			if ((Toggle & 1) || (UsingBothChannels == 0))
-				doWaterfall(FirstWaterfallChan);
-
-			if (data2)
-			{
-				for (i = 0; i < Needed; i++)
-				{
-					*ptr2++ = *data2;
-					data2 += 2;
-				}
-				if (((Toggle & 1) == 0))
-					doWaterfall(1);
-			}
-
-			if (Firstwaterfall || Secondwaterfall)
-				displayWaterfall();
-
-			remainingSamples = rx_bufsize - Needed;
-			fftCount = 0;
-
-			ptr1 = &fft_buf[0][0];
-			ptr2 = &fft_buf[1][0];
-		}
-
-		for (i = 0; i < remainingSamples; i++)
-		{
-			*ptr1++ = *data1;
-			data1 += 2;
-		}
-
-		if (data2)
-		{
-			for (i = 0; i < remainingSamples; i++)
-			{
-				*ptr2++ = *data2;
-				data2 += 2;
-			}
-		}
-		fftCount += remainingSamples;
-*/
-	}
-
-
-	if (TimerEvent == TIMER_EVENT_ON)
-	{
-		timer_event();
-//		timer_event2();
-	}
-}
-
-		/*
-
-procedure TForm1.BufferFull1(var Msg TMessage);
-var
-  i,snd_ch byte;
-begin
-  for snd_ch = 1 to 2 do
-    if pnt_change[snd_ch] then
-    begin
-      make_core_BPF(snd_ch,rx_freq[snd_ch],bpf[snd_ch]);
-      make_core_TXBPF(snd_ch,tx_freq[snd_ch],txbpf[snd_ch]);
-      pnt_change[snd_ch] = FALSE;
-    end;
-  snd_ch = 0;
-  for i = 1 to 2 do if msg.WParam = WaveOutHandle[i] then snd_ch = i;
-  if (snd_ch = 0) then exit;
-  if (snd_status[snd_ch]<>SND_TX) then exit;
-  inc(tx_buf_num[snd_ch]); if tx_buf_num[snd_ch]>tx_bufcount then tx_buf_num[snd_ch] = 1;
-  if (buf_status[snd_ch] = BUF_EMPTY) and (tx_buf_num[snd_ch] = tx_buf_num1[snd_ch]) then TX2RX(snd_ch);
-  if buf_status[snd_ch] = BUF_FULL then
-  beginf
-    make_wave_buf(snd_ch,TX_pbuf[snd_ch][tx_buf_num[snd_ch]]);
-    WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][tx_buf_num[snd_ch]],sizeof(TWaveHdr));
-    inc(tx_buf_num1[snd_ch]); if tx_buf_num1[snd_ch]>tx_bufcount then tx_buf_num1[snd_ch] = 1;
-  end;
-end;
-
-procedure TForm1.TX2RX(snd_ch byte);
-begin
-  if snd_status[snd_ch] = SND_TX then stoptx(snd_ch);
-  if snd_status[snd_ch] = SND_IDLE then begin pttoff(snd_ch); end;
-end;
-*/
-
-
-// Monitor Code - from moncode.asm
-
-
-#define	CMDBIT	4		// CURRENT MESSAGE IS A COMMAND
-#define	RESP 2		// CURRENT MSG IS RESPONSE
-#define	VER1 1 		// CURRENT MSG IS VERSION 1
-
-
-#define	UI	3
-#define	SABM 0x2F
-#define	DISC 0x43
-#define	DM	0x0F
-#define	UA	0x63
-#define	FRMR 0x87
-#define	RR	1
-#define	RNR	5
-#define	REJ	9
-
-#define	SREJ 0x0D
-#define SABME 0x6F
-#define XID 0xAF
-#define TEST 0xE3
-
-
-#define	PFBIT 0x10		// POLL/FINAL BIT IN CONTROL BYTE
-
-#define	NETROM_PID 0xCF
-#define	IP_PID 0xCC
-#define	ARP_PID 0xCD
-
-#define	NODES_SIG	0xFF
-
-char FrameData[1024] = "";
-
-char * frame_monitor(string * frame, char * code, int tx_stat)
-{
-	char mon_frm[512];
-	char AGW_path[256];
-	string * AGW_data;
-
-	const Byte * frm = "???";
-	Byte * datap;
-	Byte _data[512] = "";
-	Byte * p_data = _data;
-	int _datalen;
-
-	char  agw_port;
-	char  CallFrom[10], CallTo[10], Digi[80];
-
-	char TR = 'R';
-	char codestr[64] = "";
-
-	integer i;
-	char  time_now[32];
-	int len;
-
-	AGWUser * AGW;
-
-	Byte pid, nr, ns, f_type, f_id;
-	Byte  rpt, cr, pf;
-	Byte path[80];
-	char c;
-	const char * p;
-
-	string * data = newString();
-
-	if (code[0] && strlen(code) < 60)
-		sprintf(codestr, "[%s]", code);
-
-	if (tx_stat)
-		TR = 'T';
-
-	decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr);
-
-	datap = data->Data;
-
-	len = data->Length;
-
-	//	if (pid == 0xCF)
-	//		data = parse_NETROM(data, f_id);
-		// IP parsing
-	//	else if (pid == 0xCC)
-	//		data = parse_IP(data);
-		// ARP parsing
-	//	else if (pid == 0xCD)
-	//		data = parse_ARP(data);
-		//
-
-	if (len > 0)
-	{
-		for (i = 0; i < len; i++)
-		{
-			if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9)
-				*(p_data++) = datap[i];
-		}
-	}
-
-	_datalen = p_data - _data;
-
-	if (_datalen)
-	{
-		Byte * ptr = _data;
-		i = 0;
-
-		// remove successive cr or cr on end		while (i < _datalen)
-
-		while (i < _datalen)
-		{
-			if ((_data[i] == 13) && (_data[i + 1] == 13))
-				i++;
-			else
-				*(ptr++) = _data[i++];
-		}
-
-		if (*(ptr - 1) == 13)
-			ptr--;
-
-		*ptr = 0;
-
-		_datalen = ptr - _data;
-	}
-
-	get_monitor_path(path, CallTo, CallFrom, Digi);
-
-	if (cr)
-	{
-		c = 'C';
-		if (pf)
-			p = " P";
-		else p = "";
-	}
-	else
-	{
-		c = 'R';
-		if (pf)
-			p = " F";
-		else
-			p = "";
-	}
-
-	switch (f_id)
-	{
-	case I_I:
-
-		frm = "I";
-		break;
-
-	case S_RR:
-
-		frm = "RR";
-		break;
-
-	case S_RNR:
-
-		frm = "RNR";
-		break;
-
-	case S_REJ:
-
-		frm = "REJ";
-		break;
-
-	case S_SREJ:
-
-		frm = "SREJ";
-		break;
-
-	case U_SABM:
-
-		frm = "SABM";
-		break;
-
-	case SABME:
-
-		frm = "SABME";
-		break;
-
-	case U_DISC:
-
-		frm = "DISC";
-		break;
-
-	case U_DM:
-
-		frm = "DM";
-		break;
-
-	case U_UA:
-
-		frm = "UA";
-		break;
-
-	case U_FRMR:
-
-		frm = "FRMR";
-		break;
-
-	case U_UI:
-
-		frm = "UI";
-		break;
-
-	case U_XID:
-
-		frm = "XID";
-		break;
-
-	case U_TEST:
-
-		frm = "TEST";
-	}
-
-	if (Digi[0])
-		sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p);
-	else
-		sprintf(AGW_path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p);
-
-
-	switch (f_type)
-	{
-	case I_FRM:
-
-		//mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13;
-		sprintf(mon_frm, "%s R%d S%d pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, nr, ns, pid, len, ShortDateTime(), TR, codestr, _data);
-
-		break;
-
-	case U_FRM:
-
-		if (f_id == U_UI)
-		{
-			sprintf(mon_frm, "%s pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, pid, len, ShortDateTime(), TR, codestr, _data); // "= AGW_path + ctrl + '>' + time_now + #13;
-		}
-		else if (f_id == U_FRMR)
-		{
-			sprintf(mon_frm, "%s>%02x %02x %02x[%s]\r", AGW_path, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
-		}
-		else
-			sprintf(mon_frm, "%s>[%s%c]%s\r", AGW_path, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13;
-
-		break;
-
-	case S_FRM:
-
-		//		mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13;
-		sprintf(mon_frm, "%s R%d>[%s%c]%s\r", AGW_path, nr, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13;
-
-		break;
-
-	}
-	sprintf(FrameData, "%s", mon_frm);
-	return FrameData;
-}
-
+/*
+Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
+
+This file is part of QtSoundModem
+
+QtSoundModem is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QtSoundModem is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+newsamp
+
+You should have received a copy of the GNU General Public License
+along with QtSoundModem.  If not, see http://www.gnu.org/licenses
+
+*/
+
+// UZ7HO Soundmodem Port by John Wiseman G8BPQ
+
+#include "UZ7HOStuff.h"
+
+void make_core_BPF(UCHAR snd_ch, short freq, short width);
+void make_core_TXBPF(UCHAR snd_ch, float freq, float width);
+void make_core_INTR(UCHAR snd_ch);
+void make_core_LPF(UCHAR snd_ch, short width);
+void dw9600ProcessSample(int snd_ch, short Sample);
+void init_RUH48(int snd_ch);
+void init_RUH96(int snd_ch);
+
+char modes_name[modes_count][21] = 
+{
+	"AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd",
+	"BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd",
+	"QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd",
+	"QPSK V26A 2400bps","8PSK V27 4800bps","QPSK V26B 2400bps", "Not Available",
+	"QPSK V26A 600bps", "8PSK 900bps", "RUH 4800(DW)", "RUH 9600(DW)"
+};
+
+typedef struct wavehdr_tag {
+	unsigned short *  lpData;   /* pointer to locked data buffer */
+	int dwBufferLength;         /* length of data buffer */
+	int dwBytesRecorded;        /* used for input only */
+	int * dwUser;               /* for client's use */
+	int dwFlags;                /* assorted flags (see defines) */
+	int dwLoops;                /* loop control counter */
+	struct wavehdr_tag *lpNext; /* reserved for driver */
+	int *   reserved;           /* reserved for driver */
+} WAVEHDR, *PWAVEHDR,  * NPWAVEHDR,  * LPWAVEHDR;
+
+extern int pnt_change[5];
+int debugmode = 0;
+extern float src_buf[5][2048];
+extern Byte RCVR[5];
+
+int SatelliteMode = 0;
+
+int UDPServerPort = 8884;
+int UDPClientPort = 8888;
+int TXPort = 8884;
+
+BOOL Firstwaterfall = 1;
+BOOL Secondwaterfall = 1;
+int multiCore = FALSE;
+
+BOOL MinOnStart  =  0;
+//RS TReedSolomon;
+//  Form1 TForm1;
+//  WaveFormat TWaveFormatEx;
+int Channels = 2;
+int BitsPerSample = 16;
+float TX_Samplerate = 12000;
+float RX_Samplerate = 12000;
+int RX_SR = 11025;
+int TX_SR = 11025;
+int RX_PPM = 0;
+int TX_PPM = 0;
+int tx_bufsize = 512;
+int rx_bufsize = 512;
+int tx_bufcount = 16;
+int rx_bufcount = 16;
+int  mouse_down[2] = {0, 0};
+//UCHAR * RX_pBuf array[257];
+//  RX_header array[1..256] of TWaveHdr;
+//  TX_pBuf array[1..4,1..256] of pointer;
+//TX_header array[1..4,1..256] of TWaveHdr;
+UCHAR calib_mode[5] = {0,0,0,0};
+UCHAR snd_status [5] = {0,0,0,0};
+UCHAR buf_status [5]  = {0,0,0,0};
+UCHAR tx_buf_num1 [5] = {0,0,0,0};
+UCHAR tx_buf_num [5] = {0,0,0,0};
+
+extern short active_rx_freq[5];
+
+unsigned int pskStates[4] = {0, 0, 0, 0};	
+
+int speed[5] = {0,0,0,0};
+int panels[6] = {1,1,1,1,1};
+
+short fft_buf[2][8192];
+UCHAR fft_disp[2][1024];
+int fftCount = 0;			// FTF samples collected
+
+//  bm array[1..4] of TBitMap;
+//  bm1,bm2,bm3 TBitMap;
+
+//  WaveInHandle hWaveIn;
+//  WaveOutHandle array[1..4] of hWaveOut;
+int RXBufferLength;
+
+int grid_time = 0;
+int fft_mult = 0;
+int fft_spd = 3;
+int grid_timer = 0;
+int stop_wf  = 0;
+int raduga = DISP_RGB;
+char snd_rx_device_name[32] = "";
+char snd_tx_device_name[32] = "";
+int snd_rx_device = 0;
+int snd_tx_device = 0;
+UCHAR mod_icon_status = MOD_IDLE;
+UCHAR last_mod_icon_status = MOD_IDLE;
+UCHAR icon_timer = 0;
+//  TelIni TIniFile;
+char cur_dir[] = "";
+//  TimerId1 cardinal;
+//  TimerId2 cardinal;
+UCHAR TimerStat1 = TIMER_FREE;
+UCHAR TimerStat2 = TIMER_FREE;
+int stat_log = FALSE;
+
+int PTT_device = FALSE;
+int RX_device = FALSE;
+int TX_device = FALSE;
+int TX_rotate = FALSE;
+int UsingBothChannels = FALSE;
+int UsingLeft = FALSE;
+int UsingRight = FALSE;
+
+int SCO = FALSE;
+int DualPTT = TRUE;
+UCHAR  DebugMode = 0;
+UCHAR TimerEvent = TIMER_EVENT_OFF;
+int nr_monitor_lines = 50;
+int UTC_Time = FALSE;
+int MainPriority = 0;
+//  MainThreadHandle THandle;
+UCHAR w_state = WIN_MAXIMIZED;
+ 
+ 
+void get_filter_values(UCHAR snd_ch)
+{
+	//, unsigned short dbpf,
+//unsigned short dtxbpf,
+//unsigned short dbpftap,
+//unsigned short dlpf, 
+//unsigned short dlpftap)
+//	speed[snd_ch], bpf[snd_ch], txbpf[snd_ch], bpf_tap[snd_ch], lpf[snd_ch], lpf_tap[snd_ch]);
+
+	switch (speed[snd_ch])
+	{
+	case SPEED_8P4800:
+
+		lpf[snd_ch] = MODEM_8P4800_LPF;
+		bpf[snd_ch] = MODEM_8P4800_BPF;
+		txbpf[snd_ch] = MODEM_8P4800_TXBPF;
+		BPF_tap[snd_ch] = MODEM_8P4800_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_8P4800_LPF_TAP;
+		break;
+
+	case SPEED_MP400:
+
+		lpf[snd_ch] = MODEM_MP400_LPF;
+		bpf[snd_ch] = MODEM_MP400_BPF;
+		txbpf[snd_ch] = MODEM_MP400_TXBPF;
+		BPF_tap[snd_ch] = MODEM_MP400_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_MP400_LPF_TAP;
+
+		break;
+
+
+	case SPEED_Q4800:
+
+		lpf[snd_ch] = MODEM_Q4800_LPF;
+		bpf[snd_ch] = MODEM_Q4800_BPF;
+		txbpf[snd_ch] = MODEM_Q4800_TXBPF;
+		BPF_tap[snd_ch] = MODEM_Q4800_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_Q4800_LPF_TAP;
+
+		break;
+
+	case SPEED_Q3600:
+
+		lpf[snd_ch] = MODEM_Q3600_LPF;
+		bpf[snd_ch] = MODEM_Q3600_BPF;
+		txbpf[snd_ch] = MODEM_Q3600_TXBPF;
+		BPF_tap[snd_ch] = MODEM_Q3600_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_Q3600_LPF_TAP;
+		break;
+
+	case SPEED_Q2400:
+
+		lpf[snd_ch] = MODEM_Q2400_LPF;
+		bpf[snd_ch] = MODEM_Q2400_BPF;
+		txbpf[snd_ch] = MODEM_Q2400_TXBPF;
+		BPF_tap[snd_ch] = MODEM_Q2400_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_Q2400_LPF_TAP;
+
+		break;
+
+	case SPEED_DW2400:
+	case SPEED_2400V26B:
+
+
+		lpf[snd_ch] = MODEM_DW2400_LPF;
+		bpf[snd_ch] = MODEM_DW2400_BPF;
+		txbpf[snd_ch] = MODEM_DW2400_TXBPF;
+		BPF_tap[snd_ch] = MODEM_DW2400_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_DW2400_LPF_TAP;
+		break;
+
+	case SPEED_P2400:
+
+		lpf[snd_ch] = MODEM_P2400_LPF;
+		bpf[snd_ch] = MODEM_P2400_BPF;
+		txbpf[snd_ch] = MODEM_P2400_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P2400_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P2400_LPF_TAP;
+		break;
+
+	case SPEED_P1200:
+
+		lpf[snd_ch] = MODEM_P1200_LPF;
+		bpf[snd_ch] = MODEM_P1200_BPF;
+		txbpf[snd_ch] = MODEM_P1200_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP;
+		break;
+
+	case SPEED_P600:
+
+		lpf[snd_ch] = MODEM_P600_LPF;
+		bpf[snd_ch] = MODEM_P600_BPF;
+		txbpf[snd_ch] = MODEM_P600_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P600_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P600_LPF_TAP;
+		break;
+
+	case SPEED_P300:
+
+		lpf[snd_ch] = MODEM_P300_LPF;
+		bpf[snd_ch] = MODEM_P300_BPF;
+		txbpf[snd_ch] = MODEM_P300_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P300_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P300_LPF_TAP;
+		break;
+
+	case SPEED_300:
+
+		lpf[snd_ch] = MODEM_300_LPF;
+		bpf[snd_ch] = MODEM_300_BPF;
+		txbpf[snd_ch] = MODEM_300_TXBPF;
+		BPF_tap[snd_ch] = MODEM_300_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_300_LPF_TAP;
+
+		break;
+
+	case SPEED_600:
+
+		lpf[snd_ch] = MODEM_600_LPF;
+		bpf[snd_ch] = MODEM_600_BPF;
+		txbpf[snd_ch] = MODEM_600_TXBPF;
+		BPF_tap[snd_ch] = MODEM_600_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_600_LPF_TAP;
+
+		break;
+
+	case SPEED_1200:
+
+		lpf[snd_ch] = MODEM_1200_LPF;
+		bpf[snd_ch] = MODEM_1200_BPF;
+		txbpf[snd_ch] = MODEM_1200_TXBPF;
+		BPF_tap[snd_ch] = MODEM_1200_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_1200_LPF_TAP;
+		break;
+
+	case SPEED_2400:
+
+		lpf[snd_ch] = MODEM_2400_LPF;
+		bpf[snd_ch] = MODEM_2400_BPF;
+		txbpf[snd_ch] = MODEM_2400_TXBPF;
+		BPF_tap[snd_ch] = MODEM_2400_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_2400_LPF_TAP;
+		break;
+
+
+	case SPEED_Q300:
+	case SPEED_8PSK300:
+
+		lpf[snd_ch] = MODEM_P300_LPF;
+		bpf[snd_ch] = MODEM_P300_BPF;
+		txbpf[snd_ch] = MODEM_P300_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P300_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P300_LPF_TAP;
+
+		break;
+
+/*
+
+	case SPEED_Q1200:
+
+		lpf[snd_ch] = MODEM_P1200_LPF;
+		bpf[snd_ch] = MODEM_P1200_BPF;
+		txbpf[snd_ch] = MODEM_P1200_TXBPF;
+		BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP;
+		LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP;
+		break;
+*/
+	}
+}
+
+
+void init_2400(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_FSK;
+	rx_shift[snd_ch] = 1805;
+	rx_baudrate[snd_ch] = 2400;
+	tx_bitrate[snd_ch] = 2400;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_1200(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_FSK;
+	rx_shift[snd_ch] = 1000;
+
+	if (stdtones)
+		rx_freq[snd_ch] = 1700;
+
+	rx_baudrate[snd_ch] = 1200;
+	tx_bitrate[snd_ch] = 1200;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_600(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_FSK;
+	rx_shift[snd_ch] = 450;
+
+	rx_baudrate[snd_ch] = 600;
+	tx_bitrate[snd_ch] = 600;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_300(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_FSK;
+	rx_shift[snd_ch] = 200;
+	rx_baudrate[snd_ch] = 300;
+	tx_bitrate[snd_ch] = 300;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_MP400(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_MPSK;
+	rx_shift[snd_ch] = 175 /*sbc*/ * 3;
+	rx_baudrate[snd_ch] = 100;
+	tx_bitrate[snd_ch] = 400;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+
+void init_8P4800(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_8PSK;
+	if (stdtones)
+		rx_freq[snd_ch] = 1800;
+
+	rx_shift[snd_ch] = 1600;
+	rx_baudrate[snd_ch] = 1600;
+	tx_bitrate[snd_ch] = 4800;
+	pskStates[snd_ch] = 8;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_V26B2400(int snd_ch)
+{
+	qpsk_set[snd_ch].mode = QPSK_V26;
+	modem_mode[snd_ch] = MODE_PI4QPSK;
+
+	if (stdtones)
+		rx_freq[snd_ch] = 1800;
+
+	rx_shift[snd_ch] = 1200;
+	rx_baudrate[snd_ch] = 1200;
+	tx_bitrate[snd_ch] = 2400;
+	pskStates[snd_ch] = 8;			// Pretend 8 so quality calc works
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_DW2400(int snd_ch)
+{
+	qpsk_set[snd_ch].mode = QPSK_V26;
+	modem_mode[snd_ch] = MODE_QPSK;
+
+	if (stdtones)
+		rx_freq[snd_ch] = 1800;
+
+	rx_shift[snd_ch] = 1200;
+	rx_baudrate[snd_ch] = 1200;
+	tx_bitrate[snd_ch] = 2400;
+	pskStates[snd_ch] = 4;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_Q4800(int snd_ch)
+{
+	qpsk_set[snd_ch].mode = QPSK_SM;
+	modem_mode[snd_ch] = MODE_QPSK;
+	rx_shift[snd_ch] = 2400;
+	rx_baudrate[snd_ch] = 2400;
+	tx_bitrate[snd_ch] = 4800;
+	pskStates[snd_ch] = 4;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_Q3600(int snd_ch)
+{
+	qpsk_set[snd_ch].mode = QPSK_SM;
+	modem_mode[snd_ch] = MODE_QPSK;
+	rx_shift[snd_ch] = 1800;
+	rx_baudrate[snd_ch] = 1800;
+	tx_bitrate[snd_ch] = 3600;
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_Q2400(int snd_ch)
+{
+  qpsk_set[snd_ch].mode = QPSK_SM;
+  modem_mode[snd_ch] = MODE_QPSK;
+  rx_shift[snd_ch] = 1200;
+  rx_baudrate[snd_ch] = 1200;
+  tx_bitrate[snd_ch] = 2400;
+  pskStates[snd_ch] = 4;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_P2400(int snd_ch)
+{
+  modem_mode[snd_ch] = MODE_BPSK;
+  rx_shift[snd_ch] = 2400;
+  rx_baudrate[snd_ch] = 2400;
+  tx_bitrate[snd_ch] = 2400;
+  pskStates[snd_ch] = 2;
+  
+  if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_P1200(int snd_ch)
+{
+  modem_mode[snd_ch] = MODE_BPSK;
+  rx_shift[snd_ch] = 1200;
+  rx_baudrate[snd_ch] = 1200;
+  tx_bitrate[snd_ch] = 1200;
+  pskStates[snd_ch] = 2;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_P600(int snd_ch)
+{
+  modem_mode[snd_ch] = MODE_BPSK;
+  rx_shift[snd_ch] = 600;
+  rx_baudrate[snd_ch] = 600;
+  tx_bitrate[snd_ch] = 600;
+  pskStates[snd_ch] = 2;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_P300(int snd_ch)
+{
+  modem_mode[snd_ch] = MODE_BPSK;
+  rx_shift[snd_ch] = 300;
+  rx_baudrate[snd_ch] = 300;
+  pskStates[snd_ch] = 2;
+  tx_bitrate[snd_ch] = 300;
+
+  if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_Q300(int snd_ch)
+{
+	qpsk_set[snd_ch].mode = QPSK_V26;
+	modem_mode[snd_ch] = MODE_QPSK;
+
+	rx_shift[snd_ch] = 300;
+	rx_baudrate[snd_ch] = 300;
+	tx_bitrate[snd_ch] = 600;
+	pskStates[snd_ch] = 4;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_8PSK300(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_8PSK;
+
+	rx_shift[snd_ch] = 300;
+	rx_baudrate[snd_ch] = 300;
+	tx_bitrate[snd_ch] = 900;
+	pskStates[snd_ch] = 8;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+void init_ARDOP(int snd_ch)
+{
+	modem_mode[snd_ch] = MODE_ARDOP;
+	rx_shift[snd_ch] = 500;
+	rx_freq[snd_ch] = 1500;
+	rx_baudrate[snd_ch] = 500;
+	tx_bitrate[snd_ch] = 500;
+
+	if (modem_def[snd_ch])
+		get_filter_values(snd_ch);
+}
+
+
+void init_speed(int snd_ch);
+
+void set_speed(int snd_ch, int Modem)
+{
+	speed[snd_ch] = Modem;
+
+	init_speed(snd_ch);
+
+}
+
+int needPSKRefresh = 0;
+
+void init_speed(int snd_ch)
+{
+	int low, high;
+
+	pskStates[snd_ch] = 0;		// Not PSK
+
+	switch (speed[snd_ch])
+	{
+	case SPEED_8P4800:
+		init_8P4800(snd_ch);
+		break;
+
+	case SPEED_2400V26B:
+		init_V26B2400(snd_ch);
+
+		break;
+
+	case SPEED_DW2400:
+		init_DW2400(snd_ch);
+		break;
+
+	case SPEED_MP400:
+		init_MP400(snd_ch);
+		break;
+	case SPEED_Q4800:
+		init_Q4800(snd_ch);
+		break;
+
+	case SPEED_Q3600:
+		init_Q3600(snd_ch);
+		break;
+
+	case SPEED_Q2400:
+		init_Q2400(snd_ch);
+		break;
+
+	case SPEED_P2400:
+		init_P2400(snd_ch);
+		break;
+
+	case SPEED_P1200:
+		init_P1200(snd_ch);
+		break;
+
+//	case SPEED_Q1200:
+//		init_Q1200(snd_ch);
+//		break;
+
+	case SPEED_P600:
+		init_P600(snd_ch);
+		break;
+
+	case SPEED_P300:
+		init_P300(snd_ch);
+		break;
+
+	case SPEED_Q300:
+		init_Q300(snd_ch);
+		break;
+
+	case SPEED_8PSK300:
+		init_8PSK300(snd_ch);
+		break;
+
+	case SPEED_300:
+
+		init_300(snd_ch);
+		break;
+
+	case SPEED_600:
+
+		init_600(snd_ch);
+		break;
+
+	case SPEED_1200:
+
+		init_1200(snd_ch);
+		break;
+
+	case SPEED_2400:
+
+		init_2400(snd_ch);
+		break;
+
+	case SPEED_ARDOP:
+
+		init_ARDOP(snd_ch);
+		break;
+
+	case SPEED_RUH48:
+
+		init_RUH48(snd_ch);
+		break;
+
+	case SPEED_RUH96:
+
+		init_RUH96(snd_ch);
+		break;
+
+	}
+
+	//QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end;
+	//QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end;
+
+	if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK)
+	{
+		switch (qpsk_set[snd_ch].mode)
+		{
+		case QPSK_SM:
+
+			memcpy(&qpsk_set[snd_ch].tx[0], "\0\1\2\3", 4);
+			memcpy(&qpsk_set[snd_ch].rx[0], "\x0\x20\x40\x60", 4);
+			break;
+
+		case QPSK_V26:
+
+			memcpy(&qpsk_set[snd_ch].tx[0], "\2\3\1\0", 4);
+			memcpy(&qpsk_set[snd_ch].rx[0], "\x60\x40\0\x20", 4);
+			break;
+		}
+	}
+
+	tx_shift[snd_ch] = rx_shift[snd_ch];
+	tx_baudrate[snd_ch] = rx_baudrate[snd_ch];
+	low = roundf(rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch] + 1);
+	high = roundf(RX_Samplerate / 2 - (rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch]));
+
+	if (rx_freq[snd_ch] - low < 0)  rx_freq[snd_ch] = low;
+	if (high - rx_freq[snd_ch] < 0) rx_freq[snd_ch] = high;
+
+	tx_freq[snd_ch] = rx_freq[snd_ch];
+
+	make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
+	make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]);
+	make_core_INTR(snd_ch);
+	make_core_LPF(snd_ch, lpf[snd_ch]);
+
+	/*
+	  for i = 0 to 16 do
+		for j = 0 to nr_emph do with DET[j,i] do
+		begin
+		  minamp[snd_ch] = 0;
+		  maxamp[snd_ch] = 0;
+		  ones[snd_ch] = 0;
+		  zeros[snd_ch] = 0;
+		  sample_cnt[snd_ch] = 0;
+		  bit_cnt[snd_ch] = 0;
+		  bit_osc[snd_ch] = 0;
+		  frame_status[snd_ch] = FRAME_WAIT;
+		end;
+	  form1.show_modes;
+	  form1.show_freq_a;
+	  form1.show_freq_b;
+	  */
+	NeedWaterfallHeaders = TRUE;
+
+	CheckPSKWindows();
+}
+
+
+void  chk_snd_buf(float * buf, int len)
+{
+	word i;
+	boolean  good;
+	single prev_amp;
+
+	if (len < 2)
+		return;
+
+	good = FALSE;
+	i = 1;
+	prev_amp = buf[0];
+	do
+	{
+		if (buf[i++] != prev_amp)
+			good = TRUE;
+
+	} while (good == FALSE && i < len);
+
+	// Make noise
+	if (!good)
+		for (i = 0; i < len; i++)
+			buf[i] = rand();
+}
+
+#ifdef WIN32
+
+typedef void *HANDLE;
+typedef unsigned long DWORD;
+
+#define WINAPI __stdcall
+__declspec(dllimport)
+DWORD
+WINAPI
+WaitForSingleObject(
+	__in HANDLE hHandle,
+	__in DWORD dwMilliseconds
+);
+
+
+
+
+#define pthread_t uintptr_t
+
+uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist);
+#else
+
+#include <pthread.h>
+
+pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist)
+{
+	pthread_t thread;
+
+	if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0)
+		perror("New Thread");
+
+	return thread;
+}
+
+#endif
+
+void runModemthread(void * param);
+
+void runModems()
+{
+	int snd_ch, res;
+	pthread_t thread[4] = { 0,0,0,0 };
+
+	for (snd_ch = 0; snd_ch < 4; snd_ch++)
+	{
+		if (soundChannel[snd_ch] == 0)				// Unused channed
+			continue;	
+	
+		if (modem_mode[snd_ch] == MODE_ARDOP)
+			continue;			// Processed above
+
+		if (modem_mode[snd_ch] == MODE_RUH)
+			continue;			// Processed above
+
+		// do we need to do this again ??
+		// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
+
+		if (multiCore)			// Run modems in separate threads
+			thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch);
+		else
+			runModemthread((void *)(size_t)snd_ch);
+	}
+
+	if (multiCore)
+	{
+#ifdef WIN32
+		if (thread[0]) WaitForSingleObject(&thread[0], 2000);
+		if (thread[1]) WaitForSingleObject(&thread[1], 2000);
+		if (thread[2]) WaitForSingleObject(&thread[2], 2000);
+		if (thread[3]) WaitForSingleObject(&thread[3], 2000);
+#else
+		if (thread[0]) pthread_join(thread[0], &res);
+		if (thread[1]) pthread_join(thread[1], &res);
+		if (thread[2]) pthread_join(thread[2], &res);
+		if (thread[3]) pthread_join(thread[3], &res);
+#endif
+	}
+}
+
+Byte rcvr_idx;
+
+void runModemthread(void * param)
+{
+	int snd_ch = (int)(size_t)param;
+
+	// I want to run lowest to highest to simplify my display 
+
+	int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest
+	int lastrx = RCVR[snd_ch] * 2;
+
+	if (soundChannel[snd_ch] == 0)				// Unused channed
+		return;
+
+	for (rcvr_idx = 0; rcvr_idx <= lastrx; rcvr_idx++)
+	{
+		active_rx_freq[snd_ch] = rxOffset + chanOffset[snd_ch] + rx_freq[snd_ch] + offset;
+		offset += rcvr_offset[snd_ch];
+
+		Demodulator(snd_ch, rcvr_idx, src_buf[modemtoSoundLR[snd_ch]], rcvr_idx == lastrx, offset == 0);
+	}
+}
+
+// I think this processes a buffer of samples
+
+int Toggle = 0;
+
+void BufferFull(short * Samples, int nSamples)			// These are Stereo Samples
+{
+	word i, i1, j;
+	Byte snd_ch, rcvr_idx;
+	int buf_offset;
+	int Needed;
+	short * data1;
+	short * data2 = 0;
+
+	// if UDP server active send as UDP Datagram
+
+	if (UDPServ)	// Extract just left
+	{
+		short Buff[1024];
+
+		i1 = 0;
+
+		for (i = 0; i < rx_bufsize; i++)
+		{
+			Buff[i] = Samples[i1];
+			i1 += 2;
+		}
+
+		sendSamplestoUDP(Buff, 512, TXPort);
+	}
+
+	// Do RSID processing (can we also use this for waterfall??
+
+	RSIDProcessSamples(Samples, nSamples);
+
+	// Do FFT on every 4th buffer (2048 samples)
+
+	// if in Satellite Mode look for a Tuning signal
+
+//	if (SatelliteMode)
+//	{
+//		doTuning(Samples, nSamples);
+//	}
+
+	for (snd_ch = 0; snd_ch < 4; snd_ch++)
+	{
+		if (soundChannel[snd_ch] == 0)				// Unused channed
+			continue;
+
+		if (pnt_change[snd_ch])
+		{
+			make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]);
+			make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]);
+			pnt_change[snd_ch] = FALSE;
+		}
+
+	}
+
+	// I don't think we should process RX if either is sending
+
+	if (snd_status[0] != SND_TX && snd_status[1] != SND_TX && snd_status[2] != SND_TX && snd_status[3] != SND_TX)
+	{
+		for (snd_ch = 0; snd_ch < 4; snd_ch++)
+		{
+			if (soundChannel[snd_ch] == 0)				// Unused channed
+				continue;
+
+			if (modem_mode[snd_ch] == MODE_ARDOP)
+			{
+				short ardopbuff[1200];
+				i1 = 0;
+
+				if (using48000)
+				{
+					i1 = 0;
+					j = 0;
+
+					//	Need to downsample 48K to 12K
+					//	Try just skipping 3 samples	
+
+					nSamples /= 4;
+
+					for (i = 0; i < nSamples; i++)
+					{
+						ardopbuff[i] = Samples[i1];		
+						i1 += 8;
+					}
+				}
+				else
+				{
+					for (i = 0; i < nSamples; i++)
+					{
+						ardopbuff[i] = Samples[i1];
+						i1++;
+						i1++;
+					}
+				}
+				ARDOPProcessNewSamples(snd_ch, ardopbuff, nSamples);
+			}
+
+			else if (modem_mode[snd_ch] == MODE_RUH)
+			{
+				i1 = 0;
+				if (modemtoSoundLR[snd_ch] == 1)		// Using Right Chan
+					i1++;			
+
+				for (i = 0; i < nSamples; i++)
+				{
+					dw9600ProcessSample(snd_ch, Samples[i1]);
+					i1++;
+					i1++;
+				}
+			}
+			ProcessRXFrames(snd_ch);
+		}
+
+		// extract mono samples from data. 
+
+		data1 = Samples;
+
+		if (using48000)
+		{
+			i1 = 0;
+			j = 0;
+
+			//	Need to downsample 48K to 12K
+			//	Try just skipping 3 samples	
+
+			nSamples /= 4;
+
+			for (i = 0; i < nSamples; i++)
+			{
+				Samples[j++] = Samples[i1];
+				Samples[j++] = Samples[i1 + 1];
+				i1 += 8;
+			}
+		}
+
+		i1 = 0;
+
+		// src_buf[0] is left data,. src_buf[1] right
+
+		// We could skip extracting other channel if only one in use - is it worth it??
+
+		if (UsingBothChannels)
+		{
+			for (i = 0; i < nSamples; i++)
+			{
+				src_buf[0][i] = data1[i1];
+				i1++;
+				src_buf[1][i] = data1[i1];
+				i1++;
+			}
+		}
+		else if (UsingRight)
+		{
+			// Extract just right
+
+			i1 = 1;
+
+			for (i = 0; i < nSamples; i++)
+			{
+				src_buf[1][i] = data1[i1];
+				i1 += 2;
+			}
+		}
+		else
+		{
+			// Extract just left
+
+			for (i = 0; i < nSamples; i++)
+			{
+				src_buf[0][i] = data1[i1];
+				i1 += 2;
+			}
+		}
+
+		// Run modems before waterfall so fft buffer has values from before sync was detected
+
+		runModems();
+
+		// Do whichever waterfall is needed
+
+		// We need to run the waterfall FFT for the frequency guessing to work
+
+		// testing
+
+
+
+		int FirstWaterfallChan = 0;
+
+		doWaterfall(FirstWaterfallChan);
+		if (Firstwaterfall || Secondwaterfall)
+			displayWaterfall();
+
+
+
+
+/*
+
+		short * ptr1 = &fft_buf[0][fftCount];
+		short * ptr2 = &fft_buf[1][fftCount];
+
+		int remainingSamples = rx_bufsize;
+
+		if (UsingLeft == 0)
+		{
+			FirstWaterfallChan = 1;
+			data1++;					// to Right Samples
+		}
+
+		if (UsingBothChannels)			// Second is always Right
+			data2 = &Samples[1];		// to Right Samples
+
+
+		// FFT size isn't necessarily a multiple of rx_bufsize, so this is a bit more complicated
+		// Save FFTSize samples then process. Put the unused bits back in the fft buffer
+
+		// Collect samples for both channels if needed
+
+		Toggle++;
+
+		Needed = FFTSize - fftCount;
+
+		if (Needed <= rx_bufsize)
+		{
+			// add Needed samples to fft_buf and process. Copy rest to start of fft_buf
+
+			for (i = 0; i < Needed; i++)
+			{
+				*ptr1++ = *data1;
+				data1 += 2;
+			}
+
+			if ((Toggle & 1) || (UsingBothChannels == 0))
+				doWaterfall(FirstWaterfallChan);
+
+			if (data2)
+			{
+				for (i = 0; i < Needed; i++)
+				{
+					*ptr2++ = *data2;
+					data2 += 2;
+				}
+				if (((Toggle & 1) == 0))
+					doWaterfall(1);
+			}
+
+			if (Firstwaterfall || Secondwaterfall)
+				displayWaterfall();
+
+			remainingSamples = rx_bufsize - Needed;
+			fftCount = 0;
+
+			ptr1 = &fft_buf[0][0];
+			ptr2 = &fft_buf[1][0];
+		}
+
+		for (i = 0; i < remainingSamples; i++)
+		{
+			*ptr1++ = *data1;
+			data1 += 2;
+		}
+
+		if (data2)
+		{
+			for (i = 0; i < remainingSamples; i++)
+			{
+				*ptr2++ = *data2;
+				data2 += 2;
+			}
+		}
+		fftCount += remainingSamples;
+*/
+	}
+
+
+	if (TimerEvent == TIMER_EVENT_ON)
+	{
+		timer_event();
+//		timer_event2();
+	}
+}
+
+		/*
+
+procedure TForm1.BufferFull1(var Msg TMessage);
+var
+  i,snd_ch byte;
+begin
+  for snd_ch = 1 to 2 do
+    if pnt_change[snd_ch] then
+    begin
+      make_core_BPF(snd_ch,rx_freq[snd_ch],bpf[snd_ch]);
+      make_core_TXBPF(snd_ch,tx_freq[snd_ch],txbpf[snd_ch]);
+      pnt_change[snd_ch] = FALSE;
+    end;
+  snd_ch = 0;
+  for i = 1 to 2 do if msg.WParam = WaveOutHandle[i] then snd_ch = i;
+  if (snd_ch = 0) then exit;
+  if (snd_status[snd_ch]<>SND_TX) then exit;
+  inc(tx_buf_num[snd_ch]); if tx_buf_num[snd_ch]>tx_bufcount then tx_buf_num[snd_ch] = 1;
+  if (buf_status[snd_ch] = BUF_EMPTY) and (tx_buf_num[snd_ch] = tx_buf_num1[snd_ch]) then TX2RX(snd_ch);
+  if buf_status[snd_ch] = BUF_FULL then
+  beginf
+    make_wave_buf(snd_ch,TX_pbuf[snd_ch][tx_buf_num[snd_ch]]);
+    WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][tx_buf_num[snd_ch]],sizeof(TWaveHdr));
+    inc(tx_buf_num1[snd_ch]); if tx_buf_num1[snd_ch]>tx_bufcount then tx_buf_num1[snd_ch] = 1;
+  end;
+end;
+
+procedure TForm1.TX2RX(snd_ch byte);
+begin
+  if snd_status[snd_ch] = SND_TX then stoptx(snd_ch);
+  if snd_status[snd_ch] = SND_IDLE then begin pttoff(snd_ch); end;
+end;
+*/
+
+
+// Monitor Code - from moncode.asm
+
+
+#define	CMDBIT	4		// CURRENT MESSAGE IS A COMMAND
+#define	RESP 2		// CURRENT MSG IS RESPONSE
+#define	VER1 1 		// CURRENT MSG IS VERSION 1
+
+
+#define	UI	3
+#define	SABM 0x2F
+#define	DISC 0x43
+#define	DM	0x0F
+#define	UA	0x63
+#define	FRMR 0x87
+#define	RR	1
+#define	RNR	5
+#define	REJ	9
+
+#define	SREJ 0x0D
+#define SABME 0x6F
+#define XID 0xAF
+#define TEST 0xE3
+
+
+#define	PFBIT 0x10		// POLL/FINAL BIT IN CONTROL BYTE
+
+#define	NETROM_PID 0xCF
+#define	IP_PID 0xCC
+#define	ARP_PID 0xCD
+
+#define	NODES_SIG	0xFF
+
+char FrameData[1024] = "";
+
+char * frame_monitor(string * frame, char * code, int tx_stat)
+{
+	char mon_frm[512];
+	char AGW_path[256];
+	string * AGW_data;
+
+	const Byte * frm = "???";
+	Byte * datap;
+	Byte _data[512] = "";
+	Byte * p_data = _data;
+	int _datalen;
+
+	char  agw_port;
+	char  CallFrom[10], CallTo[10], Digi[80];
+
+	char TR = 'R';
+	char codestr[64] = "";
+
+	integer i;
+	char  time_now[32];
+	int len;
+
+	AGWUser * AGW;
+
+	Byte pid, nr, ns, f_type, f_id;
+	Byte  rpt, cr, pf;
+	Byte path[80];
+	char c;
+	const char * p;
+
+	string * data = newString();
+
+	if (code[0] && strlen(code) < 60)
+		sprintf(codestr, "[%s]", code);
+
+	if (tx_stat)
+		TR = 'T';
+
+	decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr);
+
+	datap = data->Data;
+
+	len = data->Length;
+
+	//	if (pid == 0xCF)
+	//		data = parse_NETROM(data, f_id);
+		// IP parsing
+	//	else if (pid == 0xCC)
+	//		data = parse_IP(data);
+		// ARP parsing
+	//	else if (pid == 0xCD)
+	//		data = parse_ARP(data);
+		//
+
+	if (len > 0)
+	{
+		for (i = 0; i < len; i++)
+		{
+			if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9)
+				*(p_data++) = datap[i];
+		}
+	}
+
+	_datalen = p_data - _data;
+
+	if (_datalen)
+	{
+		Byte * ptr = _data;
+		i = 0;
+
+		// remove successive cr or cr on end		while (i < _datalen)
+
+		while (i < _datalen)
+		{
+			if ((_data[i] == 13) && (_data[i + 1] == 13))
+				i++;
+			else
+				*(ptr++) = _data[i++];
+		}
+
+		if (*(ptr - 1) == 13)
+			ptr--;
+
+		*ptr = 0;
+
+		_datalen = ptr - _data;
+	}
+
+	get_monitor_path(path, CallTo, CallFrom, Digi);
+
+	if (cr)
+	{
+		c = 'C';
+		if (pf)
+			p = " P";
+		else p = "";
+	}
+	else
+	{
+		c = 'R';
+		if (pf)
+			p = " F";
+		else
+			p = "";
+	}
+
+	switch (f_id)
+	{
+	case I_I:
+
+		frm = "I";
+		break;
+
+	case S_RR:
+
+		frm = "RR";
+		break;
+
+	case S_RNR:
+
+		frm = "RNR";
+		break;
+
+	case S_REJ:
+
+		frm = "REJ";
+		break;
+
+	case S_SREJ:
+
+		frm = "SREJ";
+		break;
+
+	case U_SABM:
+
+		frm = "SABM";
+		break;
+
+	case SABME:
+
+		frm = "SABME";
+		break;
+
+	case U_DISC:
+
+		frm = "DISC";
+		break;
+
+	case U_DM:
+
+		frm = "DM";
+		break;
+
+	case U_UA:
+
+		frm = "UA";
+		break;
+
+	case U_FRMR:
+
+		frm = "FRMR";
+		break;
+
+	case U_UI:
+
+		frm = "UI";
+		break;
+
+	case U_XID:
+
+		frm = "XID";
+		break;
+
+	case U_TEST:
+
+		frm = "TEST";
+	}
+
+	if (Digi[0])
+		sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p);
+	else
+		sprintf(AGW_path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p);
+
+
+	switch (f_type)
+	{
+	case I_FRM:
+
+		//mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13;
+		sprintf(mon_frm, "%s R%d S%d pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, nr, ns, pid, len, ShortDateTime(), TR, codestr, _data);
+
+		break;
+
+	case U_FRM:
+
+		if (f_id == U_UI)
+		{
+			sprintf(mon_frm, "%s pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, pid, len, ShortDateTime(), TR, codestr, _data); // "= AGW_path + ctrl + '>' + time_now + #13;
+		}
+		else if (f_id == U_FRMR)
+		{
+			sprintf(mon_frm, "%s>%02x %02x %02x[%s]\r", AGW_path, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13;
+		}
+		else
+			sprintf(mon_frm, "%s>[%s%c]%s\r", AGW_path, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13;
+
+		break;
+
+	case S_FRM:
+
+		//		mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13;
+		sprintf(mon_frm, "%s R%d>[%s%c]%s\r", AGW_path, nr, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13;
+
+		break;
+
+	}
+	sprintf(FrameData, "%s", mon_frm);
+	return FrameData;
+}
+
diff --git a/tcpCode.cpp b/tcpCode.cpp
index d6be891..79bda8f 100644
--- a/tcpCode.cpp
+++ b/tcpCode.cpp
@@ -18,6 +18,7 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 
 */
 
+
 // UZ7HO Soundmodem Port by John Wiseman G8BPQ
 
 #include <QMessageBox>
@@ -27,15 +28,26 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 
 #define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt))
 
+QList<QTcpSocket*>  _MgmtSockets;
 QList<QTcpSocket*>  _KISSSockets;
 QList<QTcpSocket*>  _AGWSockets;
 
 QTcpServer * _KISSserver;
 QTcpServer * _AGWserver;
+QTcpServer * SixPackServer;
+QTcpSocket *SixPackSocket;
+QTcpServer * _MgmtServer;
+
+TMgmtMode ** MgmtConnections = NULL;
+int MgmtConCount = 0;
 
 extern workerThread *t;
 extern mynet m1;
+extern serialThread *serial;
 
+QString Response;
+
+extern int MgmtPort;
 extern "C" int KISSPort;
 extern "C" void * initPulse();
 extern "C" int SoundMode;
@@ -44,6 +56,10 @@ extern "C" int UDPClientPort;
 extern "C" int UDPServerPort;
 extern "C" int TXPort;
 
+extern char SixPackDevice[256];
+extern int SixPackPort;
+extern int SixPackEnable;
+
 char UDPHost[64] = "";
 
 int UDPServ = 0;				// UDP Server Active (ie broadcast sound frams as UDP packets)
@@ -68,9 +84,10 @@ extern "C"
 	void MainLoop();
 	void set_speed(int snd_ch, int Modem);
 	void init_speed(int snd_ch);
-
 }
 
+void Process6PackData(unsigned char * Bytes, int Len);
+
 extern "C" int nonGUIMode;
 
 QTimer *timer;
@@ -121,12 +138,56 @@ void mynet::start()
 		}
 	}
 
+
+	if (MgmtPort)
+	{
+		_MgmtServer = new(QTcpServer);
+
+		if (_MgmtServer->listen(QHostAddress::Any, MgmtPort))
+			connect(_MgmtServer, SIGNAL(newConnection()), this, SLOT(onMgmtConnection()));
+		else
+		{
+			if (nonGUIMode)
+				Debugprintf("Listen failed for Mgmt Port");
+			else
+			{
+				QMessageBox msgBox;
+				msgBox.setText("Listen failed for Mgmt Port.");
+				msgBox.exec();
+			}
+		}
+	}
+
+
 	QObject::connect(t, SIGNAL(sendtoKISS(void *, unsigned char *, int)), this, SLOT(sendtoKISS(void *, unsigned char *, int)), Qt::QueuedConnection);
 
 
 	QTimer *timer = new QTimer(this);
 	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
 	timer->start(100);
+
+	if (SixPackEnable)
+	{
+		if (SixPackDevice[0] && strcmp(SixPackDevice, "None") != 0)	// Using serial
+		{
+			serial->startSlave(SixPackDevice, 30000, Response);
+			serial->start();
+
+//			connect(serial, &serialThread::request, this, &QtSoundModem::showRequest);
+//			connect(serial, &serialThread::error, this, &QtSoundModem::processError);
+//			connect(serial, &serialThread::timeout, this, &QtSoundModem::processTimeout);
+
+		}
+
+		else if (SixPackPort)		// using TCP
+		{
+			SixPackServer = new(QTcpServer);
+			if (SixPackServer->listen(QHostAddress::Any, SixPackPort))
+				connect(SixPackServer, SIGNAL(newConnection()), this, SLOT(on6PackConnection()));
+
+		}
+	}
+
 }
 
 void mynet::MyTimerSlot()
@@ -172,6 +233,32 @@ void mynet::onAGWReadyRead()
 	AGW_explode_frame(sender, datas.data(), datas.length());
 }
 
+void mynet::on6PackReadyRead()
+{
+	QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
+	QByteArray datas = sender->readAll();
+	Process6PackData((unsigned char *)datas.data(), datas.length());
+}
+
+
+void mynet::on6PackSocketStateChanged(QAbstractSocket::SocketState socketState)
+{
+	if (socketState == QAbstractSocket::UnconnectedState)
+	{
+		QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
+
+	}
+}
+
+void mynet::on6PackConnection()
+{
+	QTcpSocket *clientSocket = SixPackServer->nextPendingConnection();
+	connect(clientSocket, SIGNAL(readyRead()), this, SLOT(on6PackReadyRead()));
+	connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on6PackSocketStateChanged(QAbstractSocket::SocketState)));
+
+	Debugprintf("6Pack Connect Sock %x", clientSocket);
+}
+
 
 void mynet::onKISSConnection()
 {
@@ -186,6 +273,183 @@ void mynet::onKISSConnection()
 	Debugprintf("KISS Connect Sock %x", clientSocket);
 }
 
+void Mgmt_del_socket(void * socket)
+{
+	int i;
+
+	TMgmtMode * MGMT = NULL;
+
+	if (MgmtConCount == 0)
+		return;
+
+	for (i = 0; i < MgmtConCount; i++)
+	{
+		if (MgmtConnections[i]->Socket == socket)
+		{
+			MGMT = MgmtConnections[i];
+			break;
+		}
+	}
+
+	if (MGMT == NULL)
+		return;
+
+	// Need to remove entry and move others down
+
+	MgmtConCount--;
+
+	while (i < MgmtConCount)
+	{
+		MgmtConnections[i] = MgmtConnections[i + 1];
+		i++;
+	}
+}
+
+
+
+void mynet::onMgmtConnection()
+{
+	QTcpSocket *clientSocket = (QTcpSocket *)_MgmtServer->nextPendingConnection();
+	connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onMgmtReadyRead()));
+	connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onMgmtSocketStateChanged(QAbstractSocket::SocketState)));
+
+	_MgmtSockets.append(clientSocket);
+
+	// Create a data structure to hold session info
+
+	TMgmtMode * MGMT;
+
+	MgmtConnections = (TMgmtMode **)realloc(MgmtConnections, (MgmtConCount + 1) * sizeof(void *));
+
+	MGMT = MgmtConnections[MgmtConCount++] = (TMgmtMode *)malloc(sizeof(*MGMT));
+	memset(MGMT, 0, sizeof(*MGMT));
+
+	MGMT->Socket = clientSocket;
+
+	Debugprintf("Mgmt Connect Sock %x", clientSocket);
+	clientSocket->write("Connected to QtSM\r");
+}
+
+
+void mynet::onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState)
+{
+	if (socketState == QAbstractSocket::UnconnectedState)
+	{
+		QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
+
+		Mgmt_del_socket(sender);
+
+//		free(sender->Msg);
+		_MgmtSockets.removeOne(sender);
+		Debugprintf("Mgmt Disconnect Sock %x", sender);
+	}
+}
+
+void mynet::onMgmtReadyRead()
+{
+	QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
+
+	MgmtProcessLine(sender);
+}
+
+extern "C" void SendMgmtPTT(int snd_ch, int PTTState)
+{
+	// Won't work in non=gui mode
+
+	emit m1.mgmtSetPTT(snd_ch, PTTState);
+}
+
+
+extern "C" char * strlop(char * buf, char delim);
+extern "C" char modes_name[modes_count][21];
+extern "C" int speed[5];
+
+#ifndef WIN32
+extern "C" int memicmp(char *a, char *b, int n);
+#endif
+
+void mynet::MgmtProcessLine(QTcpSocket* socket)
+{
+	// Find Session
+
+	TMgmtMode * MGMT = NULL;
+
+	for (int i = 0; i < MgmtConCount; i++)
+	{
+		if (MgmtConnections[i]->Socket == socket)
+		{
+			MGMT = MgmtConnections[i];
+			break;
+		}
+	}
+
+	if (MGMT == NULL)
+	{
+		socket->readAll();
+		return;
+	}
+
+	while (socket->bytesAvailable())
+	{
+
+		QByteArray datas = socket->peek(512);
+
+		char * Line = datas.data();
+
+		if (strchr(Line, '\r') == 0)
+			return;
+
+		char * rest = strlop(Line, '\r');
+
+		int used = rest - Line;
+
+		// Read the upto the cr Null
+
+		datas = socket->read(used);
+
+		Line = datas.data();
+		rest = strlop(Line, '\r');
+
+		if (memicmp(Line, "QtSMPort ", 8) == 0)
+		{
+			if (strlen(Line) > 10)
+			{
+				int port = atoi(&Line[8]);
+				int bpqport = atoi(&Line[10]);
+
+				if ((port > 0 && port < 5) && (bpqport > 0 && bpqport < 64))
+				{
+					MGMT->BPQPort[port - 1] = bpqport;
+					socket->write("Ok\r");
+				}
+			}
+		}
+
+		else if (memicmp(Line, "Modem ", 6) == 0)
+		{
+			int port = atoi(&Line[6]);
+
+			if (port > 0 && port < 5)
+			{
+				// if any more params - if a name follows, set it else return it
+
+				char reply[80];
+
+				sprintf(reply, "Port %d Chan %d Freq %d Modem %s \r", MGMT->BPQPort[port - 1], port, rx_freq[port - 1], modes_name[speed[port - 1]]);
+
+				socket->write(reply);
+			}
+			else
+				socket->write("Invalid Port\r");
+		}
+		else
+		{
+			socket->write("Invalid command\r");
+
+		}
+	}
+}
+
 void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState)
 {
 	if (socketState == QAbstractSocket::UnconnectedState)
@@ -195,6 +459,7 @@ void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState)
 		KISS_del_socket(sender);
 
 		_KISSSockets.removeOne(sender);
+		Debugprintf("KISS Disconnect Sock %x", sender);
 	}
 }
 
@@ -235,7 +500,7 @@ void mynet::sendtoKISS(void * sock, unsigned char * Msg, int Len)
 		QTcpSocket* socket = (QTcpSocket*)sock;
 		socket->write((char *)Msg, Len);
 	}
-//	free(Msg);
+	free(Msg);
 }
 
 
@@ -514,6 +779,36 @@ void mynet::doHLSetPTT(int c)
 
 }
 
+void mynet::domgmtSetPTT(int snd_ch, int PTTState)
+{
+	char Msg[64];
+	uint64_t ret;
+
+	for (QTcpSocket* socket : _MgmtSockets)
+	{
+		// Find Session
+
+		TMgmtMode * MGMT = NULL;
+
+		for (int i = 0; i < MgmtConCount; i++)
+		{
+			if (MgmtConnections[i]->Socket == socket)
+			{
+				MGMT = MgmtConnections[i];
+				break;
+			}
+		}
+
+		if (MGMT == NULL)
+			continue;
+
+		if (MGMT->BPQPort[snd_ch])
+		{
+			sprintf(Msg, "PTT %d %s\r", MGMT->BPQPort[snd_ch], PTTState ? "ON" : "OFF");
+			ret = socket->write(Msg);
+		}
+	}
+}
 
 
 
diff --git a/tcpCode.h b/tcpCode.h
index ea5b5a1..f0bb9a3 100644
--- a/tcpCode.h
+++ b/tcpCode.h
@@ -4,6 +4,16 @@
 
 #define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt))
 
+//class myTcpSocket : public QTcpSocket
+//{
+//public:
+//	char Msg[512];			// Received message 
+//	int Len;
+//	int BPQPort[4];			// BPQ port for each modem
+
+//};
+
+
 class mynet : public QObject
 {
 	Q_OBJECT
@@ -12,6 +22,7 @@ signals:
 
 	void HLSetPTT(int c);
 	void FLRigSetPTT(int c);
+	void mgmtSetPTT(int port, int state);
 	void startTimer(int Time);
 	void stopTimer();
 
@@ -23,11 +34,17 @@ public:
 public slots:
 	void onAGWReadyRead();
 	void onKISSSocketStateChanged(QAbstractSocket::SocketState socketState);
+	void onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState);
+	void onMgmtReadyRead();
 	void onKISSReadyRead();
 	void onAGWSocketStateChanged(QAbstractSocket::SocketState socketState);
 	void onKISSConnection();
+	void onMgmtConnection();
 	void MyTimerSlot();
 	void onAGWConnection();
+	void on6PackConnection();
+	void on6PackReadyRead();
+	void on6PackSocketStateChanged(QAbstractSocket::SocketState socketState);
 	void dropPTT();
 
 	void displayError(QAbstractSocket::SocketError socketError);
@@ -45,6 +62,7 @@ public slots:
 	void dostartTimer(int Time);
 	void dostopTimer();
 	void doHLSetPTT(int c);
+	void domgmtSetPTT(int chan, int state);
 	void doFLRigSetPTT(int c);
 
 	void readPendingDatagrams();
@@ -59,6 +77,7 @@ private:
 	int bytesReceived;
 	int TotalBytes;
 	int PayloadSize;
+	void MgmtProcessLine(QTcpSocket* socket);
 };
 
 
@@ -86,4 +105,3 @@ public:
 
 
 
-