diff --git a/MMDVM.ini b/MMDVM.ini
index 12f2b73c3..9b02e8399 100644
--- a/MMDVM.ini
+++ b/MMDVM.ini
@@ -215,6 +215,7 @@ Debug=0
[NXDN Network]
Enable=1
+Protocol=Icom
LocalAddress=127.0.0.1
LocalPort=14021
GatewayAddress=127.0.0.1
diff --git a/MMDVMHost.h b/MMDVMHost.h
index 17d6786d0..6b1e078e5 100644
--- a/MMDVMHost.h
+++ b/MMDVMHost.h
@@ -66,7 +66,7 @@ class CMMDVMHost
CDMRNetwork* m_dmrNetwork;
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
- CNXDNNetwork* m_nxdnNetwork;
+ INXDNNetwork* m_nxdnNetwork;
CPOCSAGNetwork* m_pocsagNetwork;
CDisplay* m_display;
CUMP* m_ump;
diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj
index 812c7a4a1..44b50f2c8 100644
--- a/MMDVMHost.vcxproj
+++ b/MMDVMHost.vcxproj
@@ -203,6 +203,8 @@
+
+
@@ -297,6 +299,8 @@
+
+
diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters
index fb8a3624d..01fcd3f57 100644
--- a/MMDVMHost.vcxproj.filters
+++ b/MMDVMHost.vcxproj.filters
@@ -299,6 +299,12 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
@@ -562,5 +568,11 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
index efdaba6b3..33cc6899c 100644
--- a/Makefile
+++ b/Makefile
@@ -10,10 +10,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o \
- NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o \
- POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o \
- TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o \
+ NXDNKenwoodNetwork.oNXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
+ P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o \
+ SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o \
+ YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Pi b/Makefile.Pi
index 640c07f84..d9c1acda7 100644
--- a/Makefile.Pi
+++ b/Makefile.Pi
@@ -10,10 +10,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \
- NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
- POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o \
- UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \
+ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \
+ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
+ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \
+ YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit
index 24c2907a8..a9c0b358f 100644
--- a/Makefile.Pi.Adafruit
+++ b/Makefile.Pi.Adafruit
@@ -11,10 +11,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \
- NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
- POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o \
- Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \
+ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \
+ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
+ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \
+ YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780
index c2faad28b..a0127af50 100644
--- a/Makefile.Pi.HD44780
+++ b/Makefile.Pi.HD44780
@@ -10,10 +10,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \
- NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
- POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o \
- Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \
+ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \
+ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
+ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \
+ YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED
index d2db40c73..562948933 100644
--- a/Makefile.Pi.OLED
+++ b/Makefile.Pi.OLED
@@ -10,10 +10,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \
- NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
- POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o \
- Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \
+ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \
+ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
+ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \
+ YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574
index 4c5d3a300..cd6b67f6f 100644
--- a/Makefile.Pi.PCF8574
+++ b/Makefile.Pi.PCF8574
@@ -11,10 +11,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \
- NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o \
- NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
- POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o \
- Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \
+ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \
+ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
+ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \
+ YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/Makefile.Solaris b/Makefile.Solaris
index c50d41eac..4d4698549 100644
--- a/Makefile.Solaris
+++ b/Makefile.Solaris
@@ -10,10 +10,11 @@ OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \
- NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
- NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o \
- QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o \
- UMP.o UserDB.o UserDBebtry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
+ NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o \
+ NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o \
+ P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o \
+ StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBebtry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \
+ YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
diff --git a/NXDNIcomNetwork.cpp b/NXDNIcomNetwork.cpp
new file mode 100644
index 000000000..2f3767b27
--- /dev/null
+++ b/NXDNIcomNetwork.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2009-2014,2016,2018-2020 by Jonathan Naylor G4KLX
+ *
+ * This program 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 program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "NXDNIcomNetwork.h"
+#include "NXDNDefines.h"
+#include "Defines.h"
+#include "Utils.h"
+#include "Log.h"
+
+#include
+#include
+#include
+
+const unsigned int BUFFER_LENGTH = 200U;
+
+CNXDNIcomNetwork::CNXDNIcomNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) :
+m_socket(localAddress, localPort),
+m_address(),
+m_port(gatewayPort),
+m_debug(debug),
+m_enabled(false),
+m_buffer(1000U, "NXDN Network")
+{
+ assert(gatewayPort > 0U);
+ assert(!gatewayAddress.empty());
+
+ m_address = CUDPSocket::lookup(gatewayAddress);
+}
+
+CNXDNIcomNetwork::~CNXDNIcomNetwork()
+{
+}
+
+bool CNXDNIcomNetwork::open()
+{
+ LogMessage("Opening NXDN network connection");
+
+ if (m_address.s_addr == INADDR_NONE)
+ return false;
+
+ return m_socket.open();
+}
+
+bool CNXDNIcomNetwork::write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[110U];
+ ::memset(buffer, 0x00U, 110U);
+
+ buffer[0U] = 'I';
+ buffer[1U] = 'C';
+ buffer[2U] = 'O';
+ buffer[3U] = 'M';
+ buffer[4U] = 0x01U;
+ buffer[5U] = 0x01U;
+ buffer[6U] = 0x08U;
+ buffer[7U] = 0xE0U;
+
+ switch (type) {
+ case NNMT_VOICE_HEADER:
+ case NNMT_VOICE_TRAILER:
+ buffer[37U] = 0x23U;
+ buffer[38U] = 0x1CU;
+ buffer[39U] = 0x21U;
+ break;
+ case NNMT_VOICE_BODY:
+ buffer[37U] = 0x23U;
+ buffer[38U] = 0x10U;
+ buffer[39U] = 0x21U;
+ break;
+ case NNMT_DATA_HEADER:
+ case NNMT_DATA_BODY:
+ case NNMT_DATA_TRAILER:
+ buffer[37U] = 0x23U;
+ buffer[38U] = 0x02U;
+ buffer[39U] = 0x18U;
+ break;
+ default:
+ return false;
+ }
+
+ ::memcpy(buffer + 40U, data, 33U);
+
+ if (m_debug)
+ CUtils::dump(1U, "NXDN Network Data Sent", buffer, 102U);
+
+ return m_socket.write(buffer, 102U, m_address, m_port);
+}
+
+void CNXDNIcomNetwork::clock(unsigned int ms)
+{
+ unsigned char buffer[BUFFER_LENGTH];
+
+ in_addr address;
+ unsigned int port;
+ int length = m_socket.read(buffer, BUFFER_LENGTH, address, port);
+ if (length <= 0)
+ return;
+
+ // Check if the data is for us
+ if (m_address.s_addr != address.s_addr || port != m_port) {
+ LogMessage("NXDN packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
+ return;
+ }
+
+ // Invalid packet type?
+ if (::memcmp(buffer, "ICOM", 4U) != 0)
+ return;
+
+ if (length != 102)
+ return;
+
+ if (!m_enabled)
+ return;
+
+ if (m_debug)
+ CUtils::dump(1U, "NXDN Network Data Received", buffer, length);
+
+ m_buffer.addData(buffer + 40U, 33U);
+}
+
+bool CNXDNIcomNetwork::read(unsigned char* data)
+{
+ assert(data != NULL);
+
+ if (m_buffer.isEmpty())
+ return false;
+
+ m_buffer.getData(data, 33U);
+
+ return true;
+}
+
+void CNXDNIcomNetwork::reset()
+{
+}
+
+void CNXDNIcomNetwork::close()
+{
+ m_socket.close();
+
+ LogMessage("Closing NXDN network connection");
+}
+
+void CNXDNIcomNetwork::enable(bool enabled)
+{
+ if (enabled && !m_enabled)
+ reset();
+ else if (!enabled && m_enabled)
+ m_buffer.clear();
+
+ m_enabled = enabled;
+}
diff --git a/NXDNIcomNetwork.h b/NXDNIcomNetwork.h
new file mode 100644
index 000000000..05fb0238c
--- /dev/null
+++ b/NXDNIcomNetwork.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
+ *
+ * This program 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 program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef NXDNIcomNetwork_H
+#define NXDNIcomNetwork_H
+
+#include "NXDNNetwork.h"
+#include "NXDNDefines.h"
+#include "RingBuffer.h"
+#include "UDPSocket.h"
+#include "Timer.h"
+
+#include
+#include
+
+class CNXDNIcomNetwork : public INXDNNetwork {
+public:
+ CNXDNIcomNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug);
+ virtual ~CNXDNIcomNetwork();
+
+ virtual bool open();
+
+ virtual void enable(bool enabled);
+
+ virtual bool write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type);
+
+ virtual bool read(unsigned char* data);
+
+ virtual void reset();
+
+ virtual void close();
+
+ virtual void clock(unsigned int ms);
+
+private:
+ CUDPSocket m_socket;
+ in_addr m_address;
+ unsigned int m_port;
+ bool m_debug;
+ bool m_enabled;
+ CRingBuffer m_buffer;
+};
+
+#endif
diff --git a/NXDNKenwoodNetwork.cpp b/NXDNKenwoodNetwork.cpp
new file mode 100644
index 000000000..c5a4a2060
--- /dev/null
+++ b/NXDNKenwoodNetwork.cpp
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
+ *
+ * This program 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 program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "KenwoodNetwork.h"
+#include "NXDNCRC.h"
+#include "Utils.h"
+#include "Log.h"
+
+#include
+#include
+#include
+#include
+
+const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
+
+#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
+#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
+
+const unsigned int BUFFER_LENGTH = 200U;
+
+CKenwoodNetwork::CKenwoodNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug) :
+m_rtpSocket(localPort + 0U),
+m_rtcpSocket(localPort + 1U),
+m_address(),
+m_rtcpPort(rptPort + 1U),
+m_rtpPort(rptPort + 0U),
+m_headerSeen(false),
+m_seen1(false),
+m_seen2(false),
+m_seen3(false),
+m_seen4(false),
+m_sacch(NULL),
+m_sessionId(1U),
+m_seqNo(0U),
+m_ssrc(0U),
+m_debug(debug),
+m_startSecs(0U),
+m_startUSecs(0U),
+m_rtcpTimer(1000U, 0U, 200U),
+m_hangTimer(1000U, 5U),
+m_hangType(0U),
+m_hangSrc(0U),
+m_hangDst(0U)
+{
+ assert(localPort > 0U);
+ assert(!rptAddress.empty());
+ assert(rptPort > 0U);
+
+ m_sacch = new unsigned char[10U];
+
+ m_address = CUDPSocket::lookup(rptAddress);
+}
+
+CKenwoodNetwork::~CKenwoodNetwork()
+{
+ delete[] m_sacch;
+}
+
+bool CKenwoodNetwork::open()
+{
+ LogMessage("Opening Kenwood connection");
+
+ if (m_address.s_addr == INADDR_NONE)
+ return false;
+
+ if (!m_rtcpSocket.open())
+ return false;
+
+ if (!m_rtpSocket.open()) {
+ m_rtcpSocket.close();
+ return false;
+ }
+
+ m_ssrc = m_rtpSocket.getLocalAddress();
+
+ return true;
+}
+
+bool CKenwoodNetwork::write(const unsigned char* data, unsigned int length)
+{
+ assert(data != NULL);
+
+ switch (data[0U]) {
+ case 0x81U: // Voice header or trailer
+ case 0x83U:
+ return processIcomVoiceHeader(data);
+ case 0xACU: // Voice data
+ case 0xAEU:
+ return processIcomVoiceData(data);
+ default:
+ return false;
+ }
+}
+
+bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
+{
+ assert(inData != NULL);
+
+ unsigned char outData[30U];
+ ::memset(outData, 0x00U, 30U);
+
+ // SACCH
+ outData[0U] = inData[2U];
+ outData[1U] = inData[1U];
+ outData[2U] = inData[4U] & 0xC0U;
+ outData[3U] = inData[3U];
+
+ // FACCH 1+2
+ outData[4U] = outData[14U] = inData[6U];
+ outData[5U] = outData[15U] = inData[5U];
+ outData[6U] = outData[16U] = inData[8U];
+ outData[7U] = outData[17U] = inData[7U];
+ outData[8U] = outData[18U] = inData[10U];
+ outData[9U] = outData[19U] = inData[9U];
+ outData[10U] = outData[20U] = inData[12U];
+ outData[11U] = outData[21U] = inData[11U];
+
+ unsigned short src = (inData[8U] << 8) + (inData[9U] << 0);
+ unsigned short dst = (inData[10U] << 8) + (inData[11U] << 0);
+ unsigned char type = (inData[7U] >> 5) & 0x07U;
+
+ switch (inData[5U] & 0x3FU) {
+ case 0x01U:
+ m_hangTimer.stop();
+ m_rtcpTimer.start();
+ writeRTCPStart();
+ return writeRTPVoiceHeader(outData);
+ case 0x08U: {
+ m_hangTimer.start();
+ bool ret = writeRTPVoiceTrailer(outData);
+ writeRTCPHang(type, src, dst);
+ return ret;
+ }
+ default:
+ return false;
+ }
+}
+
+bool CKenwoodNetwork::processIcomVoiceData(const unsigned char* inData)
+{
+ assert(inData != NULL);
+
+ unsigned char outData[40U], temp[10U];
+ ::memset(outData, 0x00U, 40U);
+
+ // SACCH
+ outData[0U] = inData[2U];
+ outData[1U] = inData[1U];
+ outData[2U] = inData[4U] & 0xC0U;
+ outData[3U] = inData[3U];
+
+ // Audio 1
+ ::memset(temp, 0x00U, 10U);
+ for (unsigned int i = 0U; i < 49U; i++) {
+ unsigned int offset = (5U * 8U) + i;
+ bool b = READ_BIT(inData, offset);
+ WRITE_BIT(temp, i, b);
+ }
+ outData[4U] = temp[1U];
+ outData[5U] = temp[0U];
+ outData[6U] = temp[3U];
+ outData[7U] = temp[2U];
+ outData[8U] = temp[5U];
+ outData[9U] = temp[4U];
+ outData[10U] = temp[7U];
+ outData[11U] = temp[6U];
+
+ // Audio 2
+ ::memset(temp, 0x00U, 10U);
+ for (unsigned int i = 0U; i < 49U; i++) {
+ unsigned int offset = (5U * 8U) + 49U + i;
+ bool b = READ_BIT(inData, offset);
+ WRITE_BIT(temp, i, b);
+ }
+ outData[12U] = temp[1U];
+ outData[13U] = temp[0U];
+ outData[14U] = temp[3U];
+ outData[15U] = temp[2U];
+ outData[16U] = temp[5U];
+ outData[17U] = temp[4U];
+ outData[18U] = temp[7U];
+ outData[19U] = temp[6U];
+
+ // Audio 3
+ ::memset(temp, 0x00U, 10U);
+ for (unsigned int i = 0U; i < 49U; i++) {
+ unsigned int offset = (19U * 8U) + i;
+ bool b = READ_BIT(inData, offset);
+ WRITE_BIT(temp, i, b);
+ }
+ outData[20U] = temp[1U];
+ outData[21U] = temp[0U];
+ outData[22U] = temp[3U];
+ outData[23U] = temp[2U];
+ outData[24U] = temp[5U];
+ outData[25U] = temp[4U];
+ outData[26U] = temp[7U];
+ outData[27U] = temp[6U];
+
+ // Audio 4
+ ::memset(temp, 0x00U, 10U);
+ for (unsigned int i = 0U; i < 49U; i++) {
+ unsigned int offset = (19U * 8U) + 49U + i;
+ bool b = READ_BIT(inData, offset);
+ WRITE_BIT(temp, i, b);
+ }
+ outData[28U] = temp[1U];
+ outData[29U] = temp[0U];
+ outData[30U] = temp[3U];
+ outData[31U] = temp[2U];
+ outData[32U] = temp[5U];
+ outData[33U] = temp[4U];
+ outData[34U] = temp[7U];
+ outData[35U] = temp[6U];
+
+ return writeRTPVoiceData(outData);
+}
+
+bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[50U];
+ ::memset(buffer, 0x00U, 50U);
+
+ buffer[0U] = 0x80U;
+ buffer[1U] = 0x66U;
+
+ m_seqNo++;
+ buffer[2U] = (m_seqNo >> 8) & 0xFFU;
+ buffer[3U] = (m_seqNo >> 0) & 0xFFU;
+
+ unsigned long timeStamp = getTimeStamp();
+ buffer[4U] = (timeStamp >> 24) & 0xFFU;
+ buffer[5U] = (timeStamp >> 16) & 0xFFU;
+ buffer[6U] = (timeStamp >> 8) & 0xFFU;
+ buffer[7U] = (timeStamp >> 0) & 0xFFU;
+
+ buffer[8U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[9U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[10U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[11U] = (m_ssrc >> 0) & 0xFFU;
+
+ m_sessionId++;
+ buffer[12U] = m_sessionId;
+
+ buffer[13U] = 0x00U;
+ buffer[14U] = 0x00U;
+ buffer[15U] = 0x00U;
+ buffer[16U] = 0x03U;
+ buffer[17U] = 0x03U;
+ buffer[18U] = 0x04U;
+ buffer[19U] = 0x04U;
+ buffer[20U] = 0x0AU;
+ buffer[21U] = 0x05U;
+ buffer[22U] = 0x0AU;
+
+ ::memcpy(buffer + 23U, data, 24U);
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
+
+ return m_rtpSocket.write(buffer, 47U, m_address, m_rtpPort);
+}
+
+bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[50U];
+ ::memset(buffer, 0x00U, 50U);
+
+ buffer[0U] = 0x80U;
+ buffer[1U] = 0x66U;
+
+ m_seqNo++;
+ buffer[2U] = (m_seqNo >> 8) & 0xFFU;
+ buffer[3U] = (m_seqNo >> 0) & 0xFFU;
+
+ unsigned long timeStamp = getTimeStamp();
+ buffer[4U] = (timeStamp >> 24) & 0xFFU;
+ buffer[5U] = (timeStamp >> 16) & 0xFFU;
+ buffer[6U] = (timeStamp >> 8) & 0xFFU;
+ buffer[7U] = (timeStamp >> 0) & 0xFFU;
+
+ buffer[8U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[9U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[10U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[11U] = (m_ssrc >> 0) & 0xFFU;
+
+ buffer[12U] = m_sessionId;
+
+ buffer[13U] = 0x00U;
+ buffer[14U] = 0x00U;
+ buffer[15U] = 0x00U;
+ buffer[16U] = 0x03U;
+ buffer[17U] = 0x03U;
+ buffer[18U] = 0x04U;
+ buffer[19U] = 0x04U;
+ buffer[20U] = 0x0AU;
+ buffer[21U] = 0x05U;
+ buffer[22U] = 0x0AU;
+
+ ::memcpy(buffer + 23U, data, 24U);
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
+
+ return m_rtpSocket.write(buffer, 47U, m_address, m_rtpPort);
+}
+
+bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[60U];
+ ::memset(buffer, 0x00U, 60U);
+
+ buffer[0U] = 0x80U;
+ buffer[1U] = 0x66U;
+
+ m_seqNo++;
+ buffer[2U] = (m_seqNo >> 8) & 0xFFU;
+ buffer[3U] = (m_seqNo >> 0) & 0xFFU;
+
+ unsigned long timeStamp = getTimeStamp();
+ buffer[4U] = (timeStamp >> 24) & 0xFFU;
+ buffer[5U] = (timeStamp >> 16) & 0xFFU;
+ buffer[6U] = (timeStamp >> 8) & 0xFFU;
+ buffer[7U] = (timeStamp >> 0) & 0xFFU;
+
+ buffer[8U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[9U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[10U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[11U] = (m_ssrc >> 0) & 0xFFU;
+
+ buffer[12U] = m_sessionId;
+
+ buffer[13U] = 0x00U;
+ buffer[14U] = 0x00U;
+ buffer[15U] = 0x00U;
+ buffer[16U] = 0x03U;
+ buffer[17U] = 0x02U;
+ buffer[18U] = 0x04U;
+ buffer[19U] = 0x07U;
+ buffer[20U] = 0x10U;
+ buffer[21U] = 0x08U;
+ buffer[22U] = 0x10U;
+
+ ::memcpy(buffer + 23U, data, 36U);
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 59U);
+
+ return m_rtpSocket.write(buffer, 59U, m_address, m_rtpPort);
+}
+
+bool CKenwoodNetwork::writeRTCPStart()
+{
+#if defined(_WIN32) || defined(_WIN64)
+ time_t now;
+ ::time(&now);
+
+ m_startSecs = uint32_t(now);
+
+ SYSTEMTIME st;
+ ::GetSystemTime(&st);
+
+ m_startUSecs = st.wMilliseconds * 1000U;
+#else
+ struct timeval tod;
+ ::gettimeofday(&tod, NULL);
+
+ m_startSecs = tod.tv_sec;
+ m_startUSecs = tod.tv_usec;
+#endif
+
+ unsigned char buffer[30U];
+ ::memset(buffer, 0x00U, 30U);
+
+ buffer[0U] = 0x8AU;
+ buffer[1U] = 0xCCU;
+ buffer[2U] = 0x00U;
+ buffer[3U] = 0x06U;
+
+ buffer[4U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[5U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[6U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[7U] = (m_ssrc >> 0) & 0xFFU;
+
+ buffer[8U] = 'K';
+ buffer[9U] = 'W';
+ buffer[10U] = 'N';
+ buffer[11U] = 'E';
+
+ buffer[12U] = (m_startSecs >> 24) & 0xFFU;
+ buffer[13U] = (m_startSecs >> 16) & 0xFFU;
+ buffer[14U] = (m_startSecs >> 8) & 0xFFU;
+ buffer[15U] = (m_startSecs >> 0) & 0xFFU;
+
+ buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
+ buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
+ buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
+ buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
+
+ buffer[22U] = 0x02U;
+
+ buffer[24U] = 0x01U;
+
+ buffer[27U] = 0x0AU;
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
+
+ return m_rtcpSocket.write(buffer, 28U, m_address, m_rtcpPort);
+}
+
+bool CKenwoodNetwork::writeRTCPPing()
+{
+ unsigned char buffer[30U];
+ ::memset(buffer, 0x00U, 30U);
+
+ buffer[0U] = 0x8AU;
+ buffer[1U] = 0xCCU;
+ buffer[2U] = 0x00U;
+ buffer[3U] = 0x06U;
+
+ buffer[4U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[5U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[6U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[7U] = (m_ssrc >> 0) & 0xFFU;
+
+ buffer[8U] = 'K';
+ buffer[9U] = 'W';
+ buffer[10U] = 'N';
+ buffer[11U] = 'E';
+
+ buffer[12U] = (m_startSecs >> 24) & 0xFFU;
+ buffer[13U] = (m_startSecs >> 16) & 0xFFU;
+ buffer[14U] = (m_startSecs >> 8) & 0xFFU;
+ buffer[15U] = (m_startSecs >> 0) & 0xFFU;
+
+ buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
+ buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
+ buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
+ buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
+
+ buffer[22U] = 0x02U;
+
+ buffer[24U] = 0x01U;
+
+ buffer[27U] = 0x7BU;
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
+
+ return m_rtcpSocket.write(buffer, 28U, m_address, m_rtcpPort);
+}
+
+bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
+{
+ m_hangType = type;
+ m_hangSrc = src;
+ m_hangDst = dst;
+
+ return writeRTCPHang();
+}
+
+bool CKenwoodNetwork::writeRTCPHang()
+{
+ unsigned char buffer[30U];
+ ::memset(buffer, 0x00U, 30U);
+
+ buffer[0U] = 0x8BU;
+ buffer[1U] = 0xCCU;
+ buffer[2U] = 0x00U;
+ buffer[3U] = 0x04U;
+
+ buffer[4U] = (m_ssrc >> 24) & 0xFFU;
+ buffer[5U] = (m_ssrc >> 16) & 0xFFU;
+ buffer[6U] = (m_ssrc >> 8) & 0xFFU;
+ buffer[7U] = (m_ssrc >> 0) & 0xFFU;
+
+ buffer[8U] = 'K';
+ buffer[9U] = 'W';
+ buffer[10U] = 'N';
+ buffer[11U] = 'E';
+
+ buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
+ buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
+
+ buffer[14U] = (m_hangDst >> 8) & 0xFFU;
+ buffer[15U] = (m_hangDst >> 0) & 0xFFU;
+
+ buffer[16U] = m_hangType;
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
+
+ return m_rtcpSocket.write(buffer, 20U, m_address, m_rtcpPort);
+}
+
+unsigned int CKenwoodNetwork::read(unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char dummy[BUFFER_LENGTH];
+ readRTCP(dummy);
+
+ unsigned int len = readRTP(data);
+ switch (len) {
+ case 0U: // Nothing received
+ return 0U;
+ case 35U: // Voice header or trailer
+ return processKenwoodVoiceHeader(data);
+ case 47U: // Voice data
+ if (m_headerSeen)
+ return processKenwoodVoiceData(data);
+ else
+ return processKenwoodVoiceLateEntry(data);
+ case 31U: // Data
+ return processKenwoodData(data);
+ default:
+ CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
+ return 0U;
+ }
+}
+
+unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[BUFFER_LENGTH];
+
+ in_addr address;
+ unsigned int port;
+ int length = m_rtpSocket.read(buffer, BUFFER_LENGTH, address, port);
+ if (length <= 0)
+ return 0U;
+
+ // Check if the data is for us
+ if (m_address.s_addr != address.s_addr) {
+ LogMessage("Kenwood RTP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
+ return 0U;
+ }
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
+
+ ::memcpy(data, buffer + 12U, length - 12U);
+
+ return length - 12U;
+}
+
+unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
+{
+ assert(data != NULL);
+
+ unsigned char buffer[BUFFER_LENGTH];
+
+ in_addr address;
+ unsigned int port;
+ int length = m_rtcpSocket.read(buffer, BUFFER_LENGTH, address, port);
+ if (length <= 0)
+ return 0U;
+
+ // Check if the data is for us
+ if (m_address.s_addr != address.s_addr) {
+ LogMessage("Kenwood RTCP packet received from an invalid source, %08X != %08X", m_address.s_addr, address.s_addr);
+ return 0U;
+ }
+
+ if (m_debug)
+ CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
+
+ if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
+ LogError("Missing RTCP KWNE signature");
+ return 0U;
+ }
+
+ ::memcpy(data, buffer + 12U, length - 12U);
+
+ return length - 12U;
+}
+
+void CKenwoodNetwork::close()
+{
+ m_rtcpSocket.close();
+ m_rtpSocket.close();
+
+ LogMessage("Closing Kenwood connection");
+}
+
+void CKenwoodNetwork::clock(unsigned int ms)
+{
+ m_rtcpTimer.clock(ms);
+ if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
+ if (m_hangTimer.isRunning())
+ writeRTCPHang();
+ else
+ writeRTCPPing();
+ m_rtcpTimer.start();
+ }
+
+ m_hangTimer.clock(ms);
+ if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
+ m_rtcpTimer.stop();
+ m_hangTimer.stop();
+ }
+}
+
+unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
+{
+ assert(inData != NULL);
+
+ unsigned char outData[50U], temp[20U];
+ ::memset(outData, 0x00U, 50U);
+
+ // LICH
+ outData[0U] = 0x83U;
+
+ // SACCH
+ ::memset(temp, 0x00U, 20U);
+ temp[0U] = inData[12U];
+ temp[1U] = inData[11U];
+ temp[2U] = inData[14U];
+ temp[3U] = inData[13U];
+ CNXDNCRC::encodeCRC6(temp, 26U);
+ ::memcpy(outData + 1U, temp, 4U);
+
+ // FACCH 1+2
+ ::memset(temp, 0x00U, 20U);
+ temp[0U] = inData[16U];
+ temp[1U] = inData[15U];
+ temp[2U] = inData[18U];
+ temp[3U] = inData[17U];
+ temp[4U] = inData[20U];
+ temp[5U] = inData[19U];
+ temp[6U] = inData[22U];
+ temp[7U] = inData[21U];
+ temp[8U] = inData[24U];
+ temp[9U] = inData[23U];
+ CNXDNCRC::encodeCRC12(temp, 80U);
+ ::memcpy(outData + 5U, temp, 12U);
+ ::memcpy(outData + 19U, temp, 12U);
+
+ switch (outData[5U] & 0x3FU) {
+ case 0x01U:
+ ::memcpy(inData, outData, 33U);
+ m_headerSeen = true;
+ m_seen1 = false;
+ m_seen2 = false;
+ m_seen3 = false;
+ m_seen4 = false;
+ return 33U;
+ case 0x08U:
+ ::memcpy(inData, outData, 33U);
+ m_headerSeen = false;
+ m_seen1 = false;
+ m_seen2 = false;
+ m_seen3 = false;
+ m_seen4 = false;
+ return 33U;
+ default:
+ return 0U;
+ }
+}
+
+unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
+{
+ assert(inData != NULL);
+
+ unsigned char outData[50U], temp[20U];
+ ::memset(outData, 0x00U, 50U);
+
+ // LICH
+ outData[0U] = 0xAEU;
+
+ // SACCH
+ ::memset(temp, 0x00U, 20U);
+ temp[0U] = inData[12U];
+ temp[1U] = inData[11U];
+ temp[2U] = inData[14U];
+ temp[3U] = inData[13U];
+ CNXDNCRC::encodeCRC6(temp, 26U);
+ ::memcpy(outData + 1U, temp, 4U);
+
+ // AMBE 1+2
+ unsigned int n = 5U * 8U;
+
+ temp[0U] = inData[16U];
+ temp[1U] = inData[15U];
+ temp[2U] = inData[18U];
+ temp[3U] = inData[17U];
+ temp[4U] = inData[20U];
+ temp[5U] = inData[19U];
+ temp[6U] = inData[22U];
+ temp[7U] = inData[21U];
+
+ for (unsigned int i = 0U; i < 49U; i++, n++) {
+ bool b = READ_BIT(temp, i);
+ WRITE_BIT(outData, n, b);
+ }
+
+ temp[0U] = inData[24U];
+ temp[1U] = inData[23U];
+ temp[2U] = inData[26U];
+ temp[3U] = inData[25U];
+ temp[4U] = inData[28U];
+ temp[5U] = inData[27U];
+ temp[6U] = inData[30U];
+ temp[7U] = inData[29U];
+
+ for (unsigned int i = 0U; i < 49U; i++, n++) {
+ bool b = READ_BIT(temp, i);
+ WRITE_BIT(outData, n, b);
+ }
+
+ // AMBE 3+4
+ n = 19U * 8U;
+
+ temp[0U] = inData[32U];
+ temp[1U] = inData[31U];
+ temp[2U] = inData[34U];
+ temp[3U] = inData[33U];
+ temp[4U] = inData[36U];
+ temp[5U] = inData[35U];
+ temp[6U] = inData[38U];
+ temp[7U] = inData[37U];
+
+ for (unsigned int i = 0U; i < 49U; i++, n++) {
+ bool b = READ_BIT(temp, i);
+ WRITE_BIT(outData, n, b);
+ }
+
+ temp[0U] = inData[40U];
+ temp[1U] = inData[39U];
+ temp[2U] = inData[42U];
+ temp[3U] = inData[41U];
+ temp[4U] = inData[44U];
+ temp[5U] = inData[43U];
+ temp[6U] = inData[46U];
+ temp[7U] = inData[45U];
+
+ for (unsigned int i = 0U; i < 49U; i++, n++) {
+ bool b = READ_BIT(temp, i);
+ WRITE_BIT(outData, n, b);
+ }
+
+ ::memcpy(inData, outData, 33U);
+
+ return 33U;
+}
+
+unsigned int CKenwoodNetwork::processKenwoodData(unsigned char* inData)
+{
+ if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U)
+ return 0U;
+
+ unsigned char outData[50U];
+
+ if (inData[7U] == 0x09U || inData[7U] == 0x08U) {
+ outData[0U] = 0x90U;
+ outData[1U] = inData[8U];
+ outData[2U] = inData[7U];
+ outData[3U] = inData[10U];
+ outData[4U] = inData[9U];
+ outData[5U] = inData[12U];
+ outData[6U] = inData[11U];
+ ::memcpy(inData, outData, 7U);
+ return 7U;
+ } else {
+ outData[0U] = 0x90U;
+ outData[1U] = inData[8U];
+ outData[2U] = inData[7U];
+ outData[3U] = inData[10U];
+ outData[4U] = inData[9U];
+ outData[5U] = inData[12U];
+ outData[6U] = inData[11U];
+ outData[7U] = inData[14U];
+ outData[8U] = inData[13U];
+ outData[9U] = inData[16U];
+ outData[10U] = inData[15U];
+ outData[11U] = inData[18U];
+ outData[12U] = inData[17U];
+ outData[13U] = inData[20U];
+ outData[14U] = inData[19U];
+ outData[15U] = inData[22U];
+ outData[16U] = inData[21U];
+ outData[17U] = inData[24U];
+ outData[18U] = inData[23U];
+ outData[19U] = inData[26U];
+ outData[20U] = inData[25U];
+ outData[21U] = inData[28U];
+ outData[22U] = inData[27U];
+ outData[23U] = inData[29U];
+ ::memcpy(inData, outData, 24U);
+ return 24U;
+ }
+}
+
+unsigned long CKenwoodNetwork::getTimeStamp() const
+{
+ unsigned long timeStamp = 0UL;
+
+#if defined(_WIN32) || defined(_WIN64)
+ SYSTEMTIME st;
+ ::GetSystemTime(&st);
+
+ unsigned int hh = st.wHour;
+ unsigned int mm = st.wMinute;
+ unsigned int ss = st.wSecond;
+ unsigned int ms = st.wMilliseconds;
+
+ timeStamp += hh * 3600U * 1000U * 80U;
+ timeStamp += mm * 60U * 1000U * 80U;
+ timeStamp += ss * 1000U * 80U;
+ timeStamp += ms * 80U;
+#else
+ struct timeval tod;
+ ::gettimeofday(&tod, NULL);
+
+ unsigned int ss = tod.tv_sec;
+ unsigned int ms = tod.tv_usec / 1000U;
+
+ timeStamp += ss * 1000U * 80U;
+ timeStamp += ms * 80U;
+#endif
+
+ return timeStamp;
+}
+
+unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
+{
+ assert(inData != NULL);
+
+ unsigned char sacch[4U];
+ sacch[0U] = inData[12U];
+ sacch[1U] = inData[11U];
+ sacch[2U] = inData[14U];
+ sacch[3U] = inData[13U];
+
+ switch (sacch[0U] & 0xC0U) {
+ case 0xC0U:
+ if (!m_seen1) {
+ unsigned int offset = 0U;
+ for (unsigned int i = 8U; i < 26U; i++, offset++) {
+ bool b = READ_BIT(sacch, i) != 0U;
+ WRITE_BIT(m_sacch, offset, b);
+ }
+ m_seen1 = true;
+ }
+ break;
+ case 0x80U:
+ if (!m_seen2) {
+ unsigned int offset = 18U;
+ for (unsigned int i = 8U; i < 26U; i++, offset++) {
+ bool b = READ_BIT(sacch, i) != 0U;
+ WRITE_BIT(m_sacch, offset, b);
+ }
+ m_seen2 = true;
+ }
+ break;
+ case 0x40U:
+ if (!m_seen3) {
+ unsigned int offset = 36U;
+ for (unsigned int i = 8U; i < 26U; i++, offset++) {
+ bool b = READ_BIT(sacch, i) != 0U;
+ WRITE_BIT(m_sacch, offset, b);
+ }
+ m_seen3 = true;
+ }
+ break;
+ case 0x00U:
+ if (!m_seen4) {
+ unsigned int offset = 54U;
+ for (unsigned int i = 8U; i < 26U; i++, offset++) {
+ bool b = READ_BIT(sacch, i) != 0U;
+ WRITE_BIT(m_sacch, offset, b);
+ }
+ m_seen4 = true;
+ }
+ break;
+ }
+
+ if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
+ return 0U;
+
+ // Create a dummy header
+ // Header SACCH
+ inData[11U] = 0x10U;
+ inData[12U] = 0x01U;
+ inData[13U] = 0x00U;
+ inData[14U] = 0x00U;
+
+ // Header FACCH
+ inData[15U] = m_sacch[1U];
+ inData[16U] = m_sacch[0U];
+ inData[17U] = m_sacch[3U];
+ inData[18U] = m_sacch[2U];
+ inData[19U] = m_sacch[5U];
+ inData[20U] = m_sacch[4U];
+ inData[21U] = m_sacch[7U];
+ inData[22U] = m_sacch[6U];
+ inData[23U] = 0x00U;
+ inData[24U] = m_sacch[8U];
+
+ return processKenwoodVoiceHeader(inData);
+}
diff --git a/NXDNKenwoodNetwork.h b/NXDNKenwoodNetwork.h
new file mode 100644
index 000000000..aa52d648d
--- /dev/null
+++ b/NXDNKenwoodNetwork.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
+ *
+ * This program 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 program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef KenwoodNetwork_H
+#define KenwoodNetwork_H
+
+#include "RptNetwork.h"
+#include "UDPSocket.h"
+#include "Timer.h"
+
+#include
+#include
+
+class CKenwoodNetwork : public IRptNetwork {
+public:
+ CKenwoodNetwork(unsigned int localPort, const std::string& rptAddress, unsigned int rptPort, bool debug);
+ virtual ~CKenwoodNetwork();
+
+ virtual bool open();
+
+ virtual bool write(const unsigned char* data, unsigned int length);
+
+ virtual unsigned int read(unsigned char* data);
+
+ virtual void close();
+
+ virtual void clock(unsigned int ms);
+
+private:
+ CUDPSocket m_rtpSocket;
+ CUDPSocket m_rtcpSocket;
+ in_addr m_address;
+ unsigned int m_rtcpPort;
+ unsigned int m_rtpPort;
+ bool m_headerSeen;
+ bool m_seen1;
+ bool m_seen2;
+ bool m_seen3;
+ bool m_seen4;
+ unsigned char* m_sacch;
+ uint8_t m_sessionId;
+ uint16_t m_seqNo;
+ unsigned int m_ssrc;
+ bool m_debug;
+ uint32_t m_startSecs;
+ uint32_t m_startUSecs;
+ CTimer m_rtcpTimer;
+ CTimer m_hangTimer;
+ unsigned char m_hangType;
+ unsigned short m_hangSrc;
+ unsigned short m_hangDst;
+
+ bool processIcomVoiceHeader(const unsigned char* data);
+ bool processIcomVoiceData(const unsigned char* data);
+ unsigned int processKenwoodVoiceHeader(unsigned char* data);
+ unsigned int processKenwoodVoiceData(unsigned char* data);
+ unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
+ unsigned int processKenwoodData(unsigned char* data);
+ bool writeRTPVoiceHeader(const unsigned char* data);
+ bool writeRTPVoiceData(const unsigned char* data);
+ bool writeRTPVoiceTrailer(const unsigned char* data);
+ bool writeRTCPStart();
+ bool writeRTCPPing();
+ bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
+ bool writeRTCPHang();
+ unsigned int readRTP(unsigned char* data);
+ unsigned int readRTCP(unsigned char* data);
+ unsigned long getTimeStamp() const;
+};
+
+#endif
diff --git a/NXDNNetwork.cpp b/NXDNNetwork.cpp
index 9e2690075..0f550114c 100644
--- a/NXDNNetwork.cpp
+++ b/NXDNNetwork.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014,2016,2018,2019 by Jonathan Naylor G4KLX
+ * Copyright (C) 2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,154 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include "NXDNDefines.h"
#include "NXDNNetwork.h"
-#include "Defines.h"
-#include "Utils.h"
-#include "Log.h"
-#include
-#include
-#include
-
-const unsigned int BUFFER_LENGTH = 200U;
-
-CNXDNNetwork::CNXDNNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) :
-m_socket(localAddress, localPort),
-m_address(),
-m_port(gatewayPort),
-m_debug(debug),
-m_enabled(false),
-m_buffer(1000U, "NXDN Network")
-{
- assert(gatewayPort > 0U);
- assert(!gatewayAddress.empty());
-
- m_address = CUDPSocket::lookup(gatewayAddress);
-}
-
-CNXDNNetwork::~CNXDNNetwork()
-{
-}
-
-bool CNXDNNetwork::open()
-{
- LogMessage("Opening NXDN network connection");
-
- if (m_address.s_addr == INADDR_NONE)
- return false;
-
- return m_socket.open();
-}
-
-bool CNXDNNetwork::write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type)
-{
- assert(data != NULL);
-
- unsigned char buffer[110U];
- ::memset(buffer, 0x00U, 110U);
-
- buffer[0U] = 'I';
- buffer[1U] = 'C';
- buffer[2U] = 'O';
- buffer[3U] = 'M';
- buffer[4U] = 0x01U;
- buffer[5U] = 0x01U;
- buffer[6U] = 0x08U;
- buffer[7U] = 0xE0U;
-
- switch (type) {
- case NNMT_VOICE_HEADER:
- case NNMT_VOICE_TRAILER:
- buffer[37U] = 0x23U;
- buffer[38U] = 0x1CU;
- buffer[39U] = 0x21U;
- break;
- case NNMT_VOICE_BODY:
- buffer[37U] = 0x23U;
- buffer[38U] = 0x10U;
- buffer[39U] = 0x21U;
- break;
- case NNMT_DATA_HEADER:
- case NNMT_DATA_BODY:
- case NNMT_DATA_TRAILER:
- buffer[37U] = 0x23U;
- buffer[38U] = 0x02U;
- buffer[39U] = 0x18U;
- break;
- default:
- return false;
- }
-
- ::memcpy(buffer + 40U, data, 33U);
-
- if (m_debug)
- CUtils::dump(1U, "NXDN Network Data Sent", buffer, 102U);
-
- return m_socket.write(buffer, 102U, m_address, m_port);
-}
-
-void CNXDNNetwork::clock(unsigned int ms)
+INXDNNetwork::~INXDNNetwork()
{
- unsigned char buffer[BUFFER_LENGTH];
-
- in_addr address;
- unsigned int port;
- int length = m_socket.read(buffer, BUFFER_LENGTH, address, port);
- if (length <= 0)
- return;
-
- // Check if the data is for us
- if (m_address.s_addr != address.s_addr || port != m_port) {
- LogMessage("NXDN packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
- return;
- }
-
- // Invalid packet type?
- if (::memcmp(buffer, "ICOM", 4U) != 0)
- return;
-
- if (length != 102)
- return;
-
- if (!m_enabled)
- return;
-
- if (m_debug)
- CUtils::dump(1U, "NXDN Network Data Received", buffer, length);
-
- m_buffer.addData(buffer + 40U, 33U);
-}
-
-bool CNXDNNetwork::read(unsigned char* data)
-{
- assert(data != NULL);
-
- if (m_buffer.isEmpty())
- return false;
-
- m_buffer.getData(data, 33U);
-
- return true;
-}
-
-void CNXDNNetwork::reset()
-{
-}
-
-void CNXDNNetwork::close()
-{
- m_socket.close();
-
- LogMessage("Closing NXDN network connection");
-}
-
-void CNXDNNetwork::enable(bool enabled)
-{
- if (enabled && !m_enabled)
- reset();
- else if (!enabled && m_enabled)
- m_buffer.clear();
-
- m_enabled = enabled;
}
diff --git a/NXDNNetwork.h b/NXDNNetwork.h
index 3a5b78490..564196b6c 100644
--- a/NXDNNetwork.h
+++ b/NXDNNetwork.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014,2016,2018 by Jonathan Naylor G4KLX
+ * Copyright (C) 2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,12 +20,8 @@
#define NXDNNetwork_H
#include "NXDNDefines.h"
-#include "RingBuffer.h"
-#include "UDPSocket.h"
-#include "Timer.h"
#include
-#include
enum NXDN_NETWORK_MESSAGE_TYPE {
NNMT_VOICE_HEADER,
@@ -36,32 +32,25 @@ enum NXDN_NETWORK_MESSAGE_TYPE {
NNMT_DATA_TRAILER
};
-class CNXDNNetwork {
+class INXDNNetwork {
public:
- CNXDNNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug);
- ~CNXDNNetwork();
+ virtual ~INXDNNetwork() = 0;
- bool open();
+ virtual bool open() = 0;
- void enable(bool enabled);
+ virtual void enable(bool enabled) = 0;
- bool write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type);
+ virtual bool write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type) = 0;
- bool read(unsigned char* data);
+ virtual bool read(unsigned char* data) = 0;
- void reset();
+ virtual void reset() = 0;
- void close();
+ virtual void close() = 0;
- void clock(unsigned int ms);
+ virtual void clock(unsigned int ms) = 0;
private:
- CUDPSocket m_socket;
- in_addr m_address;
- unsigned int m_port;
- bool m_debug;
- bool m_enabled;
- CRingBuffer m_buffer;
};
#endif