From e6a7ab8152566d60a680323b11db39d75e5eefc5 Mon Sep 17 00:00:00 2001
From: g8bpq <john.wiseman@cantab.net>
Date: Mon, 16 Oct 2023 18:03:48 +0100
Subject: [PATCH] 0.0.0.71

---
 Config.cpp                            |   2 +
 ModemDialog.ui                        |  52 +++++++
 QtSoundModem.cpp                      |  56 +++++--
 QtSoundModem.vcxproj.user             |   8 +-
 UZ7HOStuff.h                          |  15 +-
 ax25_agw.c                            |  49 ++++++-
 ax25_demod.c                          |   1 +
 debug/QtSoundModem.ini                | 201 --------------------------
 debug/moc_predefs-DESKTOP-MHE5LO8.h   |  12 --
 dw9600.c                              |   1 +
 il2p.c                                | 182 +++++++++++++++++++++--
 kiss_mode.c                           |   1 -
 release/moc_predefs-DESKTOP-MHE5LO8.h |  11 --
 13 files changed, 330 insertions(+), 261 deletions(-)
 delete mode 100644 debug/QtSoundModem.ini
 delete mode 100644 debug/moc_predefs-DESKTOP-MHE5LO8.h
 delete mode 100644 release/moc_predefs-DESKTOP-MHE5LO8.h

diff --git a/Config.cpp b/Config.cpp
index d9dba2f..f660aaa 100644
--- a/Config.cpp
+++ b/Config.cpp
@@ -116,6 +116,7 @@ void GetPortSettings(int Chan)
 
 	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();
@@ -350,6 +351,7 @@ void SavePortSettings(int 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]);
diff --git a/ModemDialog.ui b/ModemDialog.ui
index 00af6cd..9b5b856 100644
--- a/ModemDialog.ui
+++ b/ModemDialog.ui
@@ -737,6 +737,19 @@
          <string>Retries</string>
         </property>
        </widget>
+       <widget class="QCheckBox" name="CRC_A">
+        <property name="geometry">
+         <rect>
+          <x>200</x>
+          <y>280</y>
+          <width>53</width>
+          <height>21</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>CRC</string>
+        </property>
+       </widget>
       </widget>
      </widget>
     </widget>
@@ -1445,6 +1458,19 @@
          </property>
         </item>
        </widget>
+       <widget class="QCheckBox" name="CRC_B">
+        <property name="geometry">
+         <rect>
+          <x>200</x>
+          <y>280</y>
+          <width>53</width>
+          <height>21</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>CRC</string>
+        </property>
+       </widget>
       </widget>
       <widget class="QLabel" name="label_88">
        <property name="geometry">
@@ -2202,6 +2228,19 @@
          </rect>
         </property>
        </widget>
+       <widget class="QCheckBox" name="CRC_C">
+        <property name="geometry">
+         <rect>
+          <x>200</x>
+          <y>280</y>
+          <width>53</width>
+          <height>21</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>CRC</string>
+        </property>
+       </widget>
       </widget>
       <widget class="QComboBox" name="IL2PModeC">
        <property name="geometry">
@@ -2956,6 +2995,19 @@
          </rect>
         </property>
        </widget>
+       <widget class="QCheckBox" name="CRC_D">
+        <property name="geometry">
+         <rect>
+          <x>200</x>
+          <y>280</y>
+          <width>53</width>
+          <height>21</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>CRC</string>
+        </property>
+       </widget>
       </widget>
       <widget class="QComboBox" name="IL2PModeD">
        <property name="geometry">
diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp
index 9c0eb7b..976ca95 100644
--- a/QtSoundModem.cpp
+++ b/QtSoundModem.cpp
@@ -160,6 +160,7 @@ int WaterfallMax = 6000;
 
 int Configuring = 0;
 bool lockWaterfall = false;
+bool inWaterfall = false;
 
 extern "C" int NeedWaterfallHeaders;
 extern "C" float BinSize;
@@ -498,7 +499,7 @@ void DoPSKWindows()
 	constellationDialog->resize(NextX, 140);
 }
 
-
+QTimer *wftimer;
 
 QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
 {
@@ -763,7 +764,7 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
 	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
 	timer->start(100);
 
-	QTimer *wftimer = new QTimer(this);
+	wftimer = new QTimer(this);
 	connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF()));
 	wftimer->start(1000 * 300);
 
@@ -884,9 +885,12 @@ void QtSoundModem::MyTimerSlot()
 	{
 		NeedWaterfallHeaders = 0;
 
-		Waterfall->fill(black);
-		DrawModemFreqRange();
-		DrawFreqTicks();
+		if (Waterfall)
+		{
+			Waterfall->fill(black);
+			DrawModemFreqRange();
+			DrawFreqTicks();
+		}
 	}
 
 	show_grid();
@@ -1443,6 +1447,11 @@ void QtSoundModem::doModems()
 	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);
@@ -1532,6 +1541,12 @@ 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();
 
@@ -1661,6 +1676,11 @@ void QtSoundModem::modemSave()
 	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();
@@ -1712,7 +1732,6 @@ void QtSoundModem::modemSave()
 	for (i = 0; i < 4; i++)
 	{
 		initTStringList(&list_digi_callsigns[i]);
-
 		get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]);
 	}
 
@@ -1729,6 +1748,7 @@ void QtSoundModem::modemSave()
 	Q = Dlg->LPFWidthD->text();
 	lpf[3] = Q.toInt();
 */
+
 }
 
 void QtSoundModem::modemreject()
@@ -2401,6 +2421,14 @@ void QtSoundModem::handleButton(int Port, int Type)
 
 void QtSoundModem::doRestartWF()
 {
+	if (inWaterfall)
+	{
+		// in waterfall update thread
+
+		wftimer->start(5000);
+		return;
+	}
+		
 	lockWaterfall = true;
 
 	if (Firstwaterfall | Secondwaterfall)
@@ -2849,6 +2877,11 @@ void doWaterfallThread(void * 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
 
@@ -2865,8 +2898,6 @@ void doWaterfallThread(void * param)
 	float RealOut[8192] = { 0 };
 	float ImagOut[8192];
 
-	if (Configuring)
-		return;
 
 	RefreshLevel(CurrentLevel);	// Signal Level
 
@@ -2954,10 +2985,15 @@ void doWaterfallThread(void * param)
 	// we always do fft so we can get centre freq and do busy detect. But only upodate 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;
@@ -3005,6 +3041,8 @@ void doWaterfallThread(void * param)
 		if (TopLine > 79)
 			TopLine = 0;
 	}
+
+	inWaterfall = false;
 }
 
 
diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user
index 198fa38..8b8926c 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>2023-10-13T13:21:39.9400374Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2023-10-15T11:35:54.9027336Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="QtSettings">
-    <QtLastBackgroundBuild>2023-10-13T13:21:40.0558471Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2023-10-15T11:35:55.0741785Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <QtLastBackgroundBuild>2023-10-13T13:21:40.2121547Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2023-10-15T11:35:55.2661628Z</QtLastBackgroundBuild>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="QtSettings">
-    <QtLastBackgroundBuild>2023-10-13T13:21:40.4722944Z</QtLastBackgroundBuild>
+    <QtLastBackgroundBuild>2023-10-15T11:35:55.4659021Z</QtLastBackgroundBuild>
   </PropertyGroup>
 </Project>
\ No newline at end of file
diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h
index 00aa2f3..4a68356 100644
--- a/UZ7HOStuff.h
+++ b/UZ7HOStuff.h
@@ -4,8 +4,8 @@
 //	 My port of UZ7HO's Soundmodem
 //
 
-#define VersionString "0.0.0.70"
-#define VersionBytes {0, 0, 0, 70}
+#define VersionString "0.0.0.71"
+#define VersionBytes {0, 0, 0, 71}
 
 // Added FX25. 4x100 FEC and V27 not Working and disabled
 
@@ -174,6 +174,11 @@
 
 // .70  Restructure Waterfall area to be a single image 
 
+// .71	Add IL2P CRC Mode
+//		Improve reliability of waterfall update
+//		Report and set fx.25 and il2p flags to/from BPQ
+
+
 
 
 #include <string.h>
@@ -1004,6 +1009,7 @@ extern TAX25Port AX25Port[4][port_num];
 
 extern int fx25_mode[4];
 extern int il2p_mode[4];
+extern int il2p_crc[4];
 
 extern int tx_fx25_size[4];
 extern int tx_fx25_size_cnt[4];
@@ -1105,7 +1111,7 @@ int Add(TStringList * Q, string * Entry);
 
 struct il2p_context_s {
 
-	enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state;
+	enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_CRC, IL2P_DECODE } state;
 
 	unsigned int acc;	// Accumulate most recent 24 bits for sync word matching.
 				// Lower 8 bits are also used for accumulating bytes for
@@ -1129,6 +1135,9 @@ struct il2p_context_s {
 	int pc;			// Number of bytes placed in above.
 
 	int corrected;		// Number of symbols corrected by RS FEC.
+
+	int crccount;			// fec chars collected
+	unsigned char crc[4];	// the 4 fec chars
 };
 
 extern int NeedWaterfallHeaders;
diff --git a/ax25_agw.c b/ax25_agw.c
index 5432195..ec73b2b 100644
--- a/ax25_agw.c
+++ b/ax25_agw.c
@@ -515,13 +515,15 @@ 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[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
+	Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
 	int Len = 12;
 
 	if (Frame->DataLength == 32)
 	{
 		// BPQ to QTSM private Format. 
 
+		// First 4 Freq, 4 to 24 Modem, rest was spare. Use 27-31 for modem control flags (fx.25 il2p etc)
+
 		int Freq;
 		Byte versionBytes[4] = VersionBytes;
 
@@ -542,7 +544,7 @@ void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
 			// New Modem Name. Need to convert to index unless numeric
 
 			int n;
-			
+
 			if (strlen(&Data[4]) < 3)
 			{
 				n = atoi(&Data[4]);
@@ -569,6 +571,13 @@ void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
 			}
 		}
 
+		if (Data[27] == 2)
+		{
+			fx25_mode[Frame->Port] = Data[28];
+			il2p_mode[Frame->Port] = Data[29];
+			il2p_crc[Frame->Port] = Data[30];
+		}
+
 		// Return Freq and Modem
 
 		memcpy(&info[12], &rx_freq[Frame->Port], 2);
@@ -577,6 +586,22 @@ void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data)
 		memcpy(&info[38], versionBytes, 4);
 
 		Len = 44;
+
+		if (Data[27])
+		{
+			// BPQ understands fx25 and il2p fields
+
+			AGW->reportFreqAndModem = 2;			// Can report frequency Modem and flags
+
+
+			Len = 48;
+
+			info[44] = 1;			// Show includes Modem Flags
+			info[45] = fx25_mode[Frame->Port];
+			info[46] = il2p_mode[Frame->Port];
+			info[47] = il2p_crc[Frame->Port];
+		}
+
 		AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len));
 		return;
 	}
@@ -1337,6 +1362,9 @@ void AGW_Report_Modem_Change(int port)
 	AGWUser * AGW;
 	string * pkt;
 
+	if (soundChannel[port] == 0)		// Not in use
+		return;
+
 	// I think we send to all AGW sockets
 
 	for (i = 0; i < AGWConCount; i++)
@@ -1345,16 +1373,27 @@ void AGW_Report_Modem_Change(int port)
 
 		if (AGW->reportFreqAndModem)
 		{
-			// QTSM 's' Message with a data field is used by QtSM to set/read Modem Params
+			// QTSM 'g' Message with a data field is used by QtSM to set/read Modem Params
 
-			Byte info[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
+			Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature
+			int Len = 44;
 
 			// Return Freq and Modem
 
 			memcpy(&info[12], &rx_freq[port], 2);
 			memcpy(&info[16], modes_name[speed[port]], 20);
 			info[37] = speed[port];			// Index
-			AGW_send_to_app(AGW->socket, AGW_Gs_Frame(port, info, 44));
+
+			if (AGW->reportFreqAndModem == 2)
+			{
+				Len = 48;
+
+				info[44] = 1;			// Show includes Modem Flags
+				info[45] = fx25_mode[port];
+				info[46] = il2p_mode[port];
+				info[47] = il2p_crc[port];
+			}
+			AGW_send_to_app(AGW->socket, AGW_Gs_Frame(port, info, Len));
 		}
 	}
 }
diff --git a/ax25_demod.c b/ax25_demod.c
index 3e1b5bb..f1b0b36 100644
--- a/ax25_demod.c
+++ b/ax25_demod.c
@@ -142,6 +142,7 @@ short active_rx_freq[5] = { 1700, 1700,1700,1700,1700 };
  
 int fx25_mode[4] = { 0, 0, 0, 0 };
 int il2p_mode[4] = { 0, 0, 0, 0 };
+int il2p_crc[4] = { 0, 0, 0, 0 };
 
 int pnt_change[5] = { 0 };
 float src_buf[5][2048];
diff --git a/debug/QtSoundModem.ini b/debug/QtSoundModem.ini
deleted file mode 100644
index 0aefa4a..0000000
--- a/debug/QtSoundModem.ini
+++ /dev/null
@@ -1,201 +0,0 @@
-[General]
-geometry="@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0z\0\0\0\t\0\0\x4=\0\0\x2\xf9\0\0\0{\0\0\0(\0\0\x4<\0\0\x2\xf8\0\0\0\0\0\0\0\0\x5\0\0\0\0{\0\0\0(\0\0\x4<\0\0\x2\xf8)"
-windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\0\0\0\x3\xc2\0\0\x2\xb9\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\0)
-PSKWindow=@Rect(46 499 366 140)
-FontFamily=Courier New
-PointSize=12
-Weight=50
-
-[AX25_A]
-Retries=15
-HiToneRaise=0
-Maxframe=3
-FrackTime=5
-IdleTime=180
-SlotTime=100
-Persist=128
-RespTime=1500
-TXFrmMode=1
-FrameCollector=6
-ExcludeCallsigns=
-ExcludeAPRSFrmType=
-KISSOptimization=0
-DynamicFrack=0
-BitRecovery=0
-NonAX25Frm=0
-MEMRecovery=200
-IPOLL=80
-MyDigiCall=
-FX25=1
-IL2P=2
-RSID_UI=0
-RSID_SABM=0
-RSID_SetModem=0
-
-[Init]
-SoundMode=0
-UDPClientPort=8888
-UDPServerPort=8884
-TXPort=8884
-UDPServer=0
-UDPHost=192.168.1.255
-TXSampleRate=12000
-RXSampleRate=12000
-SndRXDeviceName="CABLE-B OUTPUT (VB-AUDIO CABLE "
-SndTXDeviceName=CABLE-B INPUT (VB-AUDIO CABLE B
-SCO=0
-DualPTT=1
-TXRotate=0
-DispMode=1
-PTT=
-PTTBAUD=19200
-PTTMode=1
-PTTOffString=
-PTTOnString=
-pttGPIOPin=17
-pttGPIOPinR=17
-CM108Addr=0xD8C:0x08
-HamLibPort=4532
-HamLibHost=127.0.0.1
-MinimizetoTray=1
-multiCore=1
-Wisdom=(fftw-3.3.5 fftwf_wisdom #x9e7d4dee #xdb14fed1 #x34bf76a4 #xeb6e8fdf\n  (fftwf_codelet_n2fv_12_avx 0 #x10048 #x10048 #x0 #xb0767e46 #x8d41dd22 #x439264a0 #x18435a99)\n  (fftwf_rdft_rank0_register 0 #x11048 #x11048 #x0 #xff75c762 #x3a0ee093 #x5b78d592 #x6b6be60e)\n  (fftwf_codelet_t2fv_2_avx 0 #x11048 #x11048 #x0 #x34057a74 #x664db78f #xa9524ebc #x606afd88)\n  (fftwf_dft_nop_register 0 #x11048 #x11048 #x0 #x4a593e24 #xb5f06ddf #xf11fe7f2 #xc010b545)\n  (fftwf_dft_bluestein_register 0 #x11048 #x11048 #x0 #x5e17c068 #x1682c5d6 #x89dd79be #x9b951c0f)\n  (fftwf_dft_vrank_geq1_register 0 #x10048 #x10048 #x0 #x2fce15e1 #x178d4f4d #x1e956a41 #xf3fd6b80)\n  (fftwf_codelet_t1fuv_4_sse2 0 #x11048 #x11048 #x0 #x2fb84bc5 #x2792028e #x8ec66ed5 #x47b5f7dc)\n  (fftwf_dft_buffered_register 0 #x11048 #x11048 #x0 #x4f8e87b4 #xec4f2fa0 #x79fe76a1 #xa16e32a5)\n  (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x8a9c355b #xb6dbadad #xbac1daac #xa866ceb3)\n  (fftwf_dft_r2hc_register 0 #x11048 #x11048 #x0 #x576d5db6 #xa6a15f8a #x875d87d5 #x7561a866)\n  (fftwf_codelet_t1fv_6_avx 0 #x10048 #x10048 #x0 #xd9db29d8 #x3302fcf3 #x19ce6e5d #x869fc341)\n  (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #xf7abab03 #x5f0e79b1 #x1b8367ad #xe5028f2c)\n  (fftwf_codelet_t1fv_12_avx 0 #x10048 #x10048 #x0 #x0b0d3933 #x08267d12 #x45613873 #xde496efe)\n)\n
-WaterfallMin=0
-WaterfallMax=3300
-
-[AX25_B]
-Retries=15
-HiToneRaise=0
-Maxframe=3
-FrackTime=5
-IdleTime=180
-SlotTime=100
-Persist=128
-RespTime=1500
-TXFrmMode=1
-FrameCollector=6
-ExcludeCallsigns=
-ExcludeAPRSFrmType=
-KISSOptimization=0
-DynamicFrack=0
-BitRecovery=0
-NonAX25Frm=0
-MEMRecovery=200
-IPOLL=80
-MyDigiCall=
-FX25=1
-IL2P=2
-RSID_UI=0
-RSID_SABM=0
-RSID_SetModem=0
-
-[Modem]
-NRRcvrPairs1=2
-NRRcvrPairs2=2
-NRRcvrPairs3=0
-NRRcvrPairs4=2
-RcvrShift1=50
-RcvrShift2=30
-RcvrShift3=30
-RcvrShift4=30
-ModemType1=4
-ModemType2=14
-ModemType3=0
-ModemType4=14
-soundChannel1=1
-soundChannel2=1
-soundChannel3=0
-soundChannel4=0
-DCDThreshold=40
-rxOffset=-100
-PreEmphasisAll1=0
-PreEmphasisAll2=0
-PreEmphasisAll3=0
-PreEmphasisAll4=0
-PreEmphasisDB1=0
-PreEmphasisDB2=0
-PreEmphasisDB3=0
-PreEmphasisDB4=0
-TxDelay1=250
-TxDelay2=250
-TxDelay3=250
-TxDelay4=250
-TxTail1=50
-TxTail2=50
-TxTail3=50
-TxTail4=50
-CWIDCall=
-CWIDInterval=0
-CWIDLeft=0
-CWIDRight=0
-CWIDType=1
-RXFreq1=1700
-RXFreq2=1700
-RXFreq3=500
-RXFreq4=1700
-CWIDMark=
-afterTraffic=false
-
-[AGWHost]
-Server=1
-Port=8000
-
-[KISS]
-Server=0
-Port=8105
-
-[AX25_C]
-Retries=15
-HiToneRaise=0
-Maxframe=3
-FrackTime=5
-IdleTime=180
-SlotTime=100
-Persist=128
-RespTime=1500
-TXFrmMode=1
-FrameCollector=6
-ExcludeCallsigns=
-ExcludeAPRSFrmType=
-KISSOptimization=0
-DynamicFrack=0
-BitRecovery=0
-NonAX25Frm=0
-MEMRecovery=200
-IPOLL=80
-MyDigiCall=
-FX25=1
-IL2P=0
-RSID_UI=0
-RSID_SABM=0
-RSID_SetModem=0
-
-[Window]
-Waterfall1=1
-Waterfall2=1
-
-[AX25_D]
-Retries=15
-HiToneRaise=0
-Maxframe=3
-FrackTime=5
-IdleTime=180
-SlotTime=100
-Persist=128
-RespTime=1500
-TXFrmMode=1
-FrameCollector=6
-ExcludeCallsigns=
-ExcludeAPRSFrmType=
-KISSOptimization=0
-DynamicFrack=0
-BitRecovery=0
-NonAX25Frm=0
-MEMRecovery=200
-IPOLL=80
-MyDigiCall=
-FX25=1
-IL2P=0
-RSID_UI=0
-RSID_SABM=0
-RSID_SetModem=0
diff --git a/debug/moc_predefs-DESKTOP-MHE5LO8.h b/debug/moc_predefs-DESKTOP-MHE5LO8.h
deleted file mode 100644
index 2c1557b..0000000
--- a/debug/moc_predefs-DESKTOP-MHE5LO8.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#define _MSC_EXTENSIONS 
-#define _INTEGRAL_MAX_BITS 64
-#define _MSC_VER 1916
-#define _MSC_FULL_VER 191627050
-#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/dw9600.c b/dw9600.c
index 8307342..407feb1 100644
--- a/dw9600.c
+++ b/dw9600.c
@@ -25,6 +25,7 @@ typedef struct TStringList_T
 
 extern int fx25_mode[4];
 extern int il2p_mode[4];
+extern int il2p_crc[4];
 extern short rx_baudrate[5];
 
 #define FX25_MODE_NONE  0
diff --git a/il2p.c b/il2p.c
index e51264c..6a284f2 100644
--- a/il2p.c
+++ b/il2p.c
@@ -42,11 +42,44 @@ along with QtSoundModem.  If not, see http://www.gnu.org/licenses
 
 // IP2P receive code (il2p_rec_bit) is called from the bit receiving code in ax25_demod.c, so includes parallel decoders
 
-
-
-
 #include "UZ7HOStuff.h"
 
+#include <stdint.h>	// for uint64_t
+
+ // Oct 2023 Nino has added an optional crc
+
+// Hamming(7,4) Encoding Table
+// Enter this table with the 4-bit value to be encoded.
+// Returns 7-bit encoded value, with high bit zero'd.
+
+uint8_t Hamming74EncodeTable[16] = { 0x0, 0x71, 0x62, 0x13, 0x54, 0x25, 0x36, 0x47, 0x38, 0x49, 0x5a, 0x2b, 0x6c, 0x1d, 0xe, 0x7f };
+
+// Hamming(7,4) Decoding Table
+// Enter this table with 7-bit encoded value, high bit masked.
+// Returns 4-bit decoded value.
+
+uint16_t Hamming74DecodeTable[128] = { \
+	  0x0, 0x0, 0x0, 0x3, 0x0, 0x5, 0xe, 0x7, \
+	  0x0, 0x9, 0xe, 0xb, 0xe, 0xd, 0xe, 0xe, \
+	  0x0, 0x3, 0x3, 0x3, 0x4, 0xd, 0x6, 0x3, \
+	  0x8, 0xd, 0xa, 0x3, 0xd, 0xd, 0xe, 0xd, \
+	  0x0, 0x5, 0x2, 0xb, 0x5, 0x5, 0x6, 0x5, \
+	  0x8, 0xb, 0xb, 0xb, 0xc, 0x5, 0xe, 0xb, \
+	  0x8, 0x1, 0x6, 0x3, 0x6, 0x5, 0x6, 0x6, \
+	  0x8, 0x8, 0x8, 0xb, 0x8, 0xd, 0x6, 0xf, \
+	  0x0, 0x9, 0x2, 0x7, 0x4, 0x7, 0x7, 0x7, \
+	  0x9, 0x9, 0xa, 0x9, 0xc, 0x9, 0xe, 0x7, \
+	  0x4, 0x1, 0xa, 0x3, 0x4, 0x4, 0x4, 0x7, \
+	  0xa, 0x9, 0xa, 0xa, 0x4, 0xd, 0xa, 0xf, \
+	  0x2, 0x1, 0x2, 0x2, 0xc, 0x5, 0x2, 0x7, \
+	  0xc, 0x9, 0x2, 0xb, 0xc, 0xc, 0xc, 0xf, \
+	  0x1, 0x1, 0x2, 0x1, 0x4, 0x1, 0x6, 0xf, \
+	  0x8, 0x1, 0xa, 0xf, 0xc, 0xf, 0xf, 0xf };
+
+
+
+
+
 void Debugprintf(const char * format, ...);
 int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count);
 
@@ -109,7 +142,6 @@ int MaxMagIndex = 0;
 #ifndef FX25_H
 #define FX25_H
 
-#include <stdint.h>	// for uint64_t
 
 extern unsigned int pskStates[4];
 
@@ -343,6 +375,7 @@ struct packet_s {
 	unsigned char frame_data[AX25_MAX_PACKET_LEN + 1];
 	/* Raw frame contents, without the CRC. */
 
+	unsigned char crc[4];		// received crc
 
 	int magic2;		/* Will get stomped on if above overflows. */
 };
@@ -1015,6 +1048,39 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t
 
 	sprintf(Mode, "IL2P %d", centreFreq);
 
+	// check crc if enabled
+
+	if (il2p_crc[snd_ch])
+	{
+		unsigned short CRCMSG;
+		unsigned short CRCCALC;
+		uint8_t crc[4];
+
+		// check crc if enabled
+
+			// The four encoded CRC bytes are arranged :
+			// | CRC3 | CRC2 | CRC1 | CRC0 | but we store as received, so F->crc[0] is CRC3
+			// CRC3 encoded from high nibble of 16 - bit CRC value (from crc2)
+			// CRC0 encoded from low nibble of 16 - bit CRC value (from crc1)
+
+		crc[0] = Hamming74DecodeTable[(pp->crc[0] & 0x7f)];
+		crc[1] = Hamming74DecodeTable[(pp->crc[1] & 0x7f)];
+		crc[2] = Hamming74DecodeTable[(pp->crc[2] & 0x7f)];
+		crc[3] = Hamming74DecodeTable[(pp->crc[3] & 0x7f)];
+
+		CRCMSG = crc[0] << 12 | crc[1] << 8 | crc[2] << 4 | crc[3];
+
+		CRCCALC = get_fcs(pp->frame_data, pp->frame_len);
+
+		if (CRCCALC != CRCMSG)
+		{
+			Debugprintf("CRC Error Decoder %d  Received %x Sent %x", subchan, CRCCALC, CRCMSG);
+	//		freeString(data);
+	//		ax25_delete(pp);
+	//		return;
+		}
+	}
+
 	stringAdd(data, pp->frame_data, pp->frame_len + 2);  // QTSM assumes a CRC
 
 	ax25_delete(pp);
@@ -3151,6 +3217,13 @@ int il2p_type_1_header(packet_t pp, int max_fec, unsigned char *hdr)
 	ax25_get_addr_no_ssid(pp, AX25_SOURCE, src_addr);
 	int src_ssid = ax25_get_ssid(pp, AX25_SOURCE);
 
+	if ((pp->frame_data[6] & 0x80) == (pp->frame_data[13] & 0x80))
+	{
+		// Both C bits are the same (ax.25 v1) so can't be sent as type 1 as will be changed
+
+		return -1;
+	}
+
 	unsigned char *a = (unsigned char *)dst_addr;
 	for (int i = 0; *a != '\0'; i++, a++) {
 		if (*a < ' ' || *a > '_') {
@@ -3919,7 +3992,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 		//assert(slice >= 0 && slice < MAX_SLICERS);
 		F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof(struct il2p_context_s));
 		//assert(F != NULL);
-		memset(F, 0, sizeof(struct il2p_context_s));
+memset(F, 0, sizeof(struct il2p_context_s));
 	}
 
 	// Accumulate most recent 24 bits received.  Most recent is LSB.
@@ -3942,7 +4015,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 			nPhases[chan][subchan][slice] = 0;
 
 			// Determine Centre Freq
-			
+
 			centreFreq[chan] = GuessCentreFreq(chan);
 		}
 		else if (__builtin_popcount((~F->acc & 0x00ffffff) ^ IL2P_SYNC_WORD) <= 1) {
@@ -3956,7 +4029,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 			centreFreq[chan] = GuessCentreFreq(chan);
 			nPhases[chan][subchan][slice] = 0;
 		}
-			
+
 		break;
 
 	case IL2P_HEADER:		// Gathering the header.
@@ -3990,7 +4063,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 					F->eplen = il2p_payload_compute(&plprop, len, max_fec);
 
 					if (il2p_get_debug() >= 1)
-					{	
+					{
 						Debugprintf("Header type %d, max fec = %d", hdr_type, max_fec);
 						Debugprintf("Need to collect %d encoded bytes for %d byte payload.", F->eplen, len);
 						Debugprintf("%d small blocks of %d and %d large blocks of %d.  %d parity symbols per block",
@@ -4008,9 +4081,21 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 						F->pc = 0;
 						F->state = IL2P_PAYLOAD;
 					}
-					else if (F->eplen == 0) {	// No payload.
+					else if (F->eplen == 0)
+					{
+						// No payload.
+
 						F->pc = 0;
-						F->state = IL2P_DECODE;
+
+						if (il2p_crc[chan])
+						{
+							// enter collect crc state
+
+							F->crccount = 0;
+							F->state = IL2P_CRC;
+						}
+						else
+							F->state = IL2P_DECODE;
 					}
 					else {			// Error.
 
@@ -4039,16 +4124,47 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 			else {
 				F->spayload[F->pc++] = (~F->acc) & 0xff;
 			}
-			if (F->pc == F->eplen) {
+			if (F->pc == F->eplen)
+			{
+				// got frame. See if need crc
 
-				// TODO?: for symmetry it seems like we should clarify the payload before combining.
+				if (il2p_crc[chan])
+				{
+					// enter collect crc state
 
-				F->state = IL2P_DECODE;
+					F->crccount = 0;
+					F->state = IL2P_CRC;
+				}
+				else
+					F->state = IL2P_DECODE;
 			}
 		}
 		break;
 
+	case IL2P_CRC:
+
+		F->bc++;
+		if (F->bc == 8)
+		{
+			// full byte has been collected.
+			F->bc = 0;
+			if (!F->polarity)
+				F->crc[F->crccount++] = F->acc & 0xff;
+			else
+				F->crc[F->crccount++] = (~F->acc) & 0xff;
+
+			if (F->crccount == 4)
+			{
+				// have all crc bytes. enter DECODE
+
+				F->state = IL2P_DECODE;
+			}
+		}
+
+			break;
+
 	case IL2P_DECODE:
+
 		// We get here after a good header and any payload has been collected.
 		// Processing is delayed by one bit but I think it makes the logic cleaner.
 		// During unit testing be sure to send an extra bit to flush it out at the end.
@@ -4077,6 +4193,18 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit)
 
 			// TODO: Could we put last 3 arguments in packet object rather than passing around separately?
 
+			// if using crc pass received crc to packet object
+
+			if (il2p_crc[chan])
+			{
+				//copy crc bytes to packet object
+
+				pp->crc[0] = F->crc[0];
+				pp->crc[1] = F->crc[1];
+				pp->crc[2] = F->crc[2];
+				pp->crc[3] = F->crc[3];
+			}
+
 			multi_modem_process_rec_packet(chan, subchan, slice, pp, alevel, retries, is_fx25, slice, centreFreq[chan]);
 		}
 	}   // end block for local variables.
@@ -4269,13 +4397,19 @@ static void send_bit(int chan, int b, int polarity);
  *
  *--------------------------------------------------------------*/
 
+
 string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity)
 {
-	unsigned char encoded[IL2P_MAX_PACKET_SIZE];
+	unsigned char encoded[IL2P_MAX_PACKET_SIZE] = "";
 	string * packet = newString();
 	int preamblecount;
 	unsigned char preamble[1024];
 
+	// The data includes the 2 byte crc but length doesn't
+
+	uint8_t crc1 = pp->frame_data[pp->frame_len];			// Low 8 bits
+	uint8_t crc2 = pp->frame_data[pp->frame_len + 1];		// High 8 bits
+
 	encoded[0] = (IL2P_SYNC_WORD >> 16) & 0xff;
 	encoded[1] = (IL2P_SYNC_WORD >> 8) & 0xff;
 	encoded[2] = (IL2P_SYNC_WORD) & 0xff;
@@ -4289,6 +4423,24 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity)
 
 	elen += IL2P_SYNC_WORD_SIZE;
 
+	// if we are using crc add it now. elen should point to end of data
+	// crc should be at pp->frame_data[pp->frame_len]
+
+	if (il2p_crc[chan])
+	{
+		// The four encoded CRC bytes are arranged :
+		// | CRC3 | CRC2 | CRC1 | CRC0 |
+
+		// CRC3 encoded from high nibble of 16 - bit CRC value (from crc2)
+		// CRC0 encoded from low nibble of 16 - bit CRC value (from crc1)
+
+		encoded[elen++] = Hamming74EncodeTable[crc2 >> 4];
+		encoded[elen++] = Hamming74EncodeTable[crc2 & 0xf];
+		encoded[elen++] = Hamming74EncodeTable[crc1 >> 4];
+		encoded[elen++] = Hamming74EncodeTable[crc1 &0xf];
+	}
+
+
 	number_of_bits_sent[chan] = 0;
 
 	if (il2p_get_debug() >= 1) {
@@ -4366,7 +4518,7 @@ string * fill_il2p_data(int snd_ch, string * data)
 	// Call il2p_send_frame to build the bit stream
 
 	pp->frame_len = data->Length - 2;					// Included CRC
-	memcpy(pp->frame_data, data->Data, data->Length);
+	memcpy(pp->frame_data, data->Data, data->Length);	// Copy the crc in case we are going to send it
 
 	result = il2p_send_frame(snd_ch, pp, 1, 0);
 
diff --git a/kiss_mode.c b/kiss_mode.c
index 5adba5d..72688a7 100644
--- a/kiss_mode.c
+++ b/kiss_mode.c
@@ -245,7 +245,6 @@ void ProcessKISSFrame(void * socket, UCHAR * Msg, int Len)
 				Add(&KISS.buffer[Chan], TXMSG);
 		}
 
-		
 		return;
 
 	case KISS_DATA:
diff --git a/release/moc_predefs-DESKTOP-MHE5LO8.h b/release/moc_predefs-DESKTOP-MHE5LO8.h
deleted file mode 100644
index 4c9c0c7..0000000
--- a/release/moc_predefs-DESKTOP-MHE5LO8.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#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 _MT 
-#define _DLL