diff --git a/Conf.cpp b/Conf.cpp index 47fe532..5e10a9f 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -239,6 +239,7 @@ m_p25LocalPort(0U), m_p25NetworkModeHang(3U), m_p25NetworkDebug(false), m_nxdnNetworkEnabled(false), +m_nxdnNetworkProtocol("Icom"), m_nxdnGatewayAddress(), m_nxdnGatewayPort(0U), m_nxdnLocalAddress(), @@ -857,6 +858,8 @@ bool CConf::read() } else if (section == SECTION_NXDN_NETWORK) { if (::strcmp(key, "Enable") == 0) m_nxdnNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Protocol") == 0) + m_nxdnNetworkProtocol = value; else if (::strcmp(key, "LocalAddress") == 0) m_nxdnLocalAddress = value; else if (::strcmp(key, "LocalPort") == 0) @@ -1865,6 +1868,11 @@ bool CConf::getNXDNNetworkEnabled() const return m_nxdnNetworkEnabled; } +std::string CConf::getNXDNNetworkProtocol() const +{ + return m_nxdnNetworkProtocol; +} + std::string CConf::getNXDNGatewayAddress() const { return m_nxdnGatewayAddress; diff --git a/Conf.h b/Conf.h index 91d7118..ef584c7 100644 --- a/Conf.h +++ b/Conf.h @@ -247,6 +247,7 @@ public: // The NXDN Network section bool getNXDNNetworkEnabled() const; + std::string getNXDNNetworkProtocol() const; std::string getNXDNGatewayAddress() const; unsigned int getNXDNGatewayPort() const; std::string getNXDNLocalAddress() const; @@ -522,6 +523,7 @@ private: bool m_p25NetworkDebug; bool m_nxdnNetworkEnabled; + std::string m_nxdnNetworkProtocol; std::string m_nxdnGatewayAddress; unsigned int m_nxdnGatewayPort; std::string m_nxdnLocalAddress; diff --git a/Display.cpp b/Display.cpp index 0b47b64..d0e6fd0 100644 --- a/Display.cpp +++ b/Display.cpp @@ -566,7 +566,7 @@ CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) } } else { SERIAL_SPEED baudrate = SERIAL_9600; - if (screenLayout==4U) + if (screenLayout&0x0cU) baudrate = SERIAL_115200; LogInfo(" Display baudrate: %u ",baudrate); diff --git a/MMDVM.ini b/MMDVM.ini index 6f2b910..e6de0fb 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -218,6 +218,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.cpp b/MMDVMHost.cpp index 3cf60cb..ebc5613 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -17,6 +17,8 @@ */ #include "MMDVMHost.h" +#include "NXDNKenwoodNetwork.h" +#include "NXDNIcomNetwork.h" #include "RSSIInterpolator.h" #include "SerialController.h" #include "Version.h" @@ -1504,6 +1506,7 @@ bool CMMDVMHost::createP25Network() bool CMMDVMHost::createNXDNNetwork() { + std::string protocol = m_conf.getNXDNNetworkProtocol(); std::string gatewayAddress = m_conf.getNXDNGatewayAddress(); unsigned int gatewayPort = m_conf.getNXDNGatewayPort(); std::string localAddress = m_conf.getNXDNLocalAddress(); @@ -1512,13 +1515,17 @@ bool CMMDVMHost::createNXDNNetwork() bool debug = m_conf.getNXDNNetworkDebug(); LogInfo("NXDN Network Parameters"); + LogInfo(" Protocol: %s", protocol.c_str()); LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); LogInfo(" Gateway Port: %u", gatewayPort); LogInfo(" Local Address: %s", localAddress.c_str()); LogInfo(" Local Port: %u", localPort); LogInfo(" Mode Hang: %us", m_nxdnNetModeHang); - m_nxdnNetwork = new CNXDNNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + if (protocol == "Kenwood") + m_nxdnNetwork = new CNXDNKenwoodNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + else + m_nxdnNetwork = new CNXDNIcomNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); bool ret = m_nxdnNetwork->open(); if (!ret) { @@ -2099,22 +2106,32 @@ void CMMDVMHost::remoteControl() case RCD_ENABLE_DSTAR: if (m_dstar != NULL && m_dstarEnabled==false) processEnableCommand(m_dstarEnabled, true); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(true); break; case RCD_ENABLE_DMR: if (m_dmr != NULL && m_dmrEnabled==false) processEnableCommand(m_dmrEnabled, true); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(true); break; case RCD_ENABLE_YSF: if (m_ysf != NULL && m_ysfEnabled==false) processEnableCommand(m_ysfEnabled, true); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(true); break; case RCD_ENABLE_P25: if (m_p25 != NULL && m_p25Enabled==false) processEnableCommand(m_p25Enabled, true); + if (m_p25Network != NULL) + m_p25Network->enable(true); break; case RCD_ENABLE_NXDN: if (m_nxdn != NULL && m_nxdnEnabled==false) processEnableCommand(m_nxdnEnabled, true); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(true); break; case RCD_ENABLE_FM: if (!m_fmEnabled) @@ -2127,22 +2144,32 @@ void CMMDVMHost::remoteControl() case RCD_DISABLE_DSTAR: if (m_dstar != NULL && m_dstarEnabled==true) processEnableCommand(m_dstarEnabled, false); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); break; case RCD_DISABLE_DMR: if (m_dmr != NULL && m_dmrEnabled==true) processEnableCommand(m_dmrEnabled, false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); break; case RCD_DISABLE_YSF: if (m_ysf != NULL && m_ysfEnabled==true) processEnableCommand(m_ysfEnabled, false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); break; case RCD_DISABLE_P25: if (m_p25 != NULL && m_p25Enabled==true) processEnableCommand(m_p25Enabled, false); + if (m_p25Network != NULL) + m_p25Network->enable(false); break; case RCD_DISABLE_NXDN: if (m_nxdn != NULL && m_nxdnEnabled==true) processEnableCommand(m_nxdnEnabled, false); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(false); break; case RCD_DISABLE_FM: if (m_fmEnabled == true) diff --git a/MMDVMHost.h b/MMDVMHost.h index 1a3f791..64b1413 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -69,7 +69,7 @@ private: CDMRNetwork* m_dmrNetwork; CYSFNetwork* m_ysfNetwork; CP25Network* m_p25Network; - CNXDNNetwork* m_nxdnNetwork; + INXDNNetwork* m_nxdnNetwork; CPOCSAGNetwork* m_pocsagNetwork; CAX25Network* m_ax25Network; CDisplay* m_display; diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 63b8aa6..693adb1 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -205,6 +205,8 @@ + + @@ -301,6 +303,8 @@ + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index b333302..b4bee02 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -305,6 +305,12 @@ Header Files + + Header Files + + + Header Files + @@ -574,5 +580,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index 2ac2552..64f966f 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 b/Makefile.Pi index 3a4c2c1..b6e813f 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -10,10 +10,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 16775bb..6f9dd91 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -11,10 +11,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 3d02d71..0c73316 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -10,10 +10,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 f4f2024..078a266 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -10,10 +10,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 3a70077..0c5c478 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -11,10 +11,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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 d469fad..f7bb03d 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -10,10 +10,11 @@ OBJECTS = AX25Control.o AX25Network.o \ 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/NXDNControl.cpp b/NXDNControl.cpp index 1f3fa4c..1e200c4 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2020 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 @@ -39,7 +39,7 @@ const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04 #define WRITE_BIT1(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_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) -CNXDNControl::CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, CNXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper) : +CNXDNControl::CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper) : m_ran(ran), m_id(id), m_selfOnly(selfOnly), diff --git a/NXDNControl.h b/NXDNControl.h index 5c441e2..af2fba1 100644 --- a/NXDNControl.h +++ b/NXDNControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015-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 @@ -36,7 +36,7 @@ class CNXDNControl { public: - CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, CNXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper); + CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper); ~CNXDNControl(); bool writeModem(unsigned char* data, unsigned int len); @@ -53,7 +53,7 @@ private: unsigned int m_ran; unsigned int m_id; bool m_selfOnly; - CNXDNNetwork* m_network; + INXDNNetwork* m_network; CDisplay* m_display; bool m_duplex; bool m_remoteGateway; diff --git a/NXDNIcomNetwork.cpp b/NXDNIcomNetwork.cpp new file mode 100644 index 0000000..2f3767b --- /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 0000000..05fb023 --- /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 0000000..8531f90 --- /dev/null +++ b/NXDNKenwoodNetwork.cpp @@ -0,0 +1,1171 @@ +/* + * 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 "NXDNKenwoodNetwork.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; + +CNXDNKenwoodNetwork::CNXDNKenwoodNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, bool debug) : +m_rtpSocket(localAddress, localPort + 0U), +m_rtcpSocket(localAddress, localPort + 1U), +m_address(), +m_rtcpPort(gwyPort + 1U), +m_rtpPort(gwyPort + 0U), +m_enabled(false), +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(!gwyAddress.empty()); + assert(gwyPort > 0U); + + m_sacch = new unsigned char[10U]; + + m_address = CUDPSocket::lookup(gwyAddress); +} + +CNXDNKenwoodNetwork::~CNXDNKenwoodNetwork() +{ + delete[] m_sacch; +} + +bool CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type) +{ + assert(data != NULL); + + switch (type) { + case NNMT_VOICE_HEADER: // Voice header or trailer + case NNMT_VOICE_TRAILER: + return processIcomVoiceHeader(data); + case NNMT_VOICE_BODY: // Voice data + return processIcomVoiceData(data); + case NNMT_DATA_HEADER: // Data header or trailer + case NNMT_DATA_TRAILER: + return processIcomDataHeader(data); + case NNMT_DATA_BODY: // Data data + return processIcomDataData(data); + default: + return false; + } +} + +bool CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::processIcomDataHeader(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 0x09U: + m_hangTimer.stop(); + m_rtcpTimer.start(); + writeRTCPStart(); + return writeRTPDataHeader(outData); + case 0x08U: { + m_hangTimer.start(); + bool ret = writeRTPDataTrailer(outData); + writeRTCPHang(type, src, dst); + return ret; + } + default: + return false; + } +} + +bool CNXDNKenwoodNetwork::processIcomDataData(const unsigned char* inData) +{ + assert(inData != NULL); + + unsigned char outData[40U]; + ::memset(outData, 0x00U, 40U); + + outData[0U] = inData[2U]; + outData[1U] = inData[1U]; + outData[2U] = inData[4U]; + outData[3U] = inData[3U]; + outData[4U] = inData[6U]; + outData[5U] = inData[5U]; + outData[6U] = inData[8U]; + outData[7U] = inData[7U]; + outData[8U] = inData[10U]; + outData[9U] = inData[9U]; + outData[10U] = inData[12U]; + outData[11U] = inData[11U]; + outData[12U] = inData[14U]; + outData[13U] = inData[13U]; + outData[14U] = inData[16U]; + outData[15U] = inData[15U]; + outData[16U] = inData[18U]; + outData[17U] = inData[17U]; + outData[18U] = inData[20U]; + outData[19U] = inData[19U]; + outData[20U] = inData[22U]; + outData[21U] = inData[21U]; + + return writeRTPDataData(outData); +} + +bool CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::writeRTPDataHeader(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] = 0x01U; + buffer[17U] = 0x01U; + + ::memcpy(buffer + 18U, data, 24U); + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 42U); + + return m_rtpSocket.write(buffer, 42U, m_address, m_rtpPort); +} + +bool CNXDNKenwoodNetwork::writeRTPDataTrailer(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] = 0x01U; + buffer[17U] = 0x06U; + + ::memcpy(buffer + 18U, data, 24U); + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 42U); + + return m_rtpSocket.write(buffer, 42U, m_address, m_rtpPort); +} + +bool CNXDNKenwoodNetwork::writeRTPDataData(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] = 0x01U; + buffer[17U] = 0x01U; + + ::memcpy(buffer + 18U, data, 24U); + + if (m_debug) + CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 42U); + + return m_rtpSocket.write(buffer, 42U, m_address, m_rtpPort); +} + +bool CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::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 CNXDNKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst) +{ + m_hangType = type; + m_hangSrc = src; + m_hangDst = dst; + + return writeRTCPHang(); +} + +bool CNXDNKenwoodNetwork::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); +} + +bool CNXDNKenwoodNetwork::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 false; + 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 false; + } +} + +unsigned int CNXDNKenwoodNetwork::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_enabled) + 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 CNXDNKenwoodNetwork::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_enabled) + 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 CNXDNKenwoodNetwork::reset() +{ + m_rtcpTimer.stop(); + m_hangTimer.stop(); + + m_headerSeen = false; + m_seen1 = false; + m_seen2 = false; + m_seen3 = false; + m_seen4 = false; +} + +void CNXDNKenwoodNetwork::close() +{ + m_rtcpSocket.close(); + m_rtpSocket.close(); + + LogMessage("Closing Kenwood connection"); +} + +void CNXDNKenwoodNetwork::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(); + } +} + +bool CNXDNKenwoodNetwork::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 true; + case 0x08U: + ::memcpy(inData, outData, 33U); + m_headerSeen = false; + m_seen1 = false; + m_seen2 = false; + m_seen3 = false; + m_seen4 = false; + return true; + default: + return false; + } +} + +bool CNXDNKenwoodNetwork::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 true; +} + +bool CNXDNKenwoodNetwork::processKenwoodData(unsigned char* inData) +{ + if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U) + return false; + + 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]; + 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]; + ::memcpy(inData, outData, 20U); + return true; + } 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 true; + } +} + +unsigned long CNXDNKenwoodNetwork::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; +} + +bool CNXDNKenwoodNetwork::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 false; + + // 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); +} + +void CNXDNKenwoodNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + + m_enabled = enabled; +} diff --git a/NXDNKenwoodNetwork.h b/NXDNKenwoodNetwork.h new file mode 100644 index 0000000..e0c1099 --- /dev/null +++ b/NXDNKenwoodNetwork.h @@ -0,0 +1,97 @@ +/* + * 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 NXDNKenwoodNetwork_H +#define NXDNKenwoodNetwork_H + +#include "NXDNNetwork.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CNXDNKenwoodNetwork : public INXDNNetwork { +public: + CNXDNKenwoodNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, bool debug); + virtual ~CNXDNKenwoodNetwork(); + + 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_rtpSocket; + CUDPSocket m_rtcpSocket; + in_addr m_address; + unsigned int m_rtcpPort; + unsigned int m_rtpPort; + bool m_enabled; + 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); + bool processIcomDataHeader(const unsigned char* data); + bool processIcomDataData(const unsigned char* data); + bool processIcomDataTrailer(const unsigned char* data); + bool processKenwoodVoiceHeader(unsigned char* data); + bool processKenwoodVoiceData(unsigned char* data); + bool processKenwoodVoiceLateEntry(unsigned char* data); + bool processKenwoodData(unsigned char* data); + bool writeRTPVoiceHeader(const unsigned char* data); + bool writeRTPVoiceData(const unsigned char* data); + bool writeRTPVoiceTrailer(const unsigned char* data); + bool writeRTPDataHeader(const unsigned char* data); + bool writeRTPDataData(const unsigned char* data); + bool writeRTPDataTrailer(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 9e26900..0f55011 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() +INXDNNetwork::~INXDNNetwork() { } - -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) -{ - 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 3a5b784..564196b 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 diff --git a/Nextion.cpp b/Nextion.cpp index f15d6cf..adb8e76 100644 --- a/Nextion.cpp +++ b/Nextion.cpp @@ -38,6 +38,16 @@ const unsigned int P25_BER_COUNT = 7U; // 7 * 180ms = 1260ms const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms const unsigned int NXDN_BER_COUNT = 28U; // 28 * 40ms = 1120ms +#define LAYOUT_COMPAT_MASK (7 << 0) // compatibility for old setting +#define LAYOUT_TA_ENABLE (1 << 4) // enable Talker Alias (TA) display +#define LAYOUT_TA_COLOUR (1 << 5) // TA display with font colour change +#define LAYOUT_TA_FONTSIZE (1 << 6) // TA display with font size change +#define LAYOUT_DIY (1 << 7) // use ON7LDS-DIY layout + +// bit[3:2] is used in Display.cpp to set connection speed for LCD panel. +// 00:low, others:high-speed. bit[2] is overlapped with LAYOUT_COMPAT_MASK. +#define LAYOUT_HIGHSPEED (3 << 2) + CNextion::CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout, unsigned int txFrequency, unsigned int rxFrequency, bool displayTempInF, const std::string& location) : CDisplay(), m_callsign(callsign), @@ -49,7 +59,7 @@ m_mode(MODE_IDLE), m_displayClock(displayClock), m_utc(utc), m_idleBrightness(idleBrightness), -m_screenLayout(screenLayout), +m_screenLayout(0), m_clockDisplayTimer(1000U, 0U, 400U), m_rssiAccum1(0U), m_rssiAccum2(0U), @@ -68,6 +78,23 @@ m_location(location) { assert(serial != NULL); assert(brightness >= 0U && brightness <= 100U); + + static const unsigned int feature_set[] = { + 0, // 0: G4KLX + 0, // 1: (reserved, low speed) + // 2: ON7LDS + LAYOUT_TA_ENABLE | LAYOUT_TA_COLOUR | LAYOUT_TA_FONTSIZE, + LAYOUT_TA_ENABLE | LAYOUT_DIY, // 3: ON7LDS-DIY + LAYOUT_TA_ENABLE | LAYOUT_DIY, // 4: ON7LDS-DIY (high speed) + 0, // 5: (reserved, high speed) + 0, // 6: (reserved, high speed) + 0, // 7: (reserved, high speed) + }; + + if (screenLayout & ~LAYOUT_COMPAT_MASK) + m_screenLayout = screenLayout & ~LAYOUT_COMPAT_MASK; + else + m_screenLayout = feature_set[screenLayout]; } CNextion::~CNextion() @@ -118,7 +145,7 @@ void CNextion::setIdleInt() ::sprintf(command, "t0.txt=\"%s/%u\"", m_callsign.c_str(), m_dmrid); sendCommand(command); - if (m_screenLayout > 2U) { + if (m_screenLayout & LAYOUT_DIY) { ::sprintf(command, "t4.txt=\"%s\"", m_callsign.c_str()); sendCommand(command); ::sprintf(command, "t5.txt=\"%u\"", m_dmrid); @@ -350,17 +377,21 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro if (slotNo == 1U) { - if (m_screenLayout == 2U) { - sendCommand("t2.pco=0"); - sendCommand("t2.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t2.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t2.font=4"); } sendCommand("t2.txt=\"2 Listening\""); sendCommandAction(69U); } else { - if (m_screenLayout == 2U) { - sendCommand("t0.pco=0"); - sendCommand("t0.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t0.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t0.font=4"); } sendCommand("t0.txt=\"1 Listening\""); @@ -377,9 +408,11 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro if (slotNo == 1U) { ::sprintf(text, "t0.txt=\"1 %s %s\"", type, src.c_str()); - if (m_screenLayout == 2U) { - sendCommand("t0.pco=0"); - sendCommand("t0.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t0.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t0.font=4"); } sendCommand(text); @@ -391,9 +424,11 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro } else { ::sprintf(text, "t2.txt=\"2 %s %s\"", type, src.c_str()); - if (m_screenLayout == 2U) { - sendCommand("t2.pco=0"); - sendCommand("t2.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t2.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t2.font=4"); } sendCommand(text); @@ -448,15 +483,17 @@ void CNextion::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type) { - if (m_screenLayout < 2U) + if (!(m_screenLayout & LAYOUT_TA_ENABLE)) return; if (type[0] == ' ') { if (slotNo == 1U) { - if (m_screenLayout == 2U) sendCommand("t0.pco=33808"); + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t0.pco=33808"); sendCommandAction(64U); } else { - if (m_screenLayout == 2U) sendCommand("t2.pco=33808"); + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t2.pco=33808"); sendCommandAction(72U); } @@ -467,33 +504,36 @@ void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, co char text[50U]; ::sprintf(text, "t0.txt=\"1 %s %s\"", type, talkerAlias); - if (m_screenLayout == 2U) { + if (m_screenLayout & LAYOUT_TA_FONTSIZE) { if (::strlen((char*)talkerAlias) > (16U-4U)) sendCommand("t0.font=3"); if (::strlen((char*)talkerAlias) > (20U-4U)) sendCommand("t0.font=2"); if (::strlen((char*)talkerAlias) > (24U-4U)) sendCommand("t0.font=1"); - - sendCommand("t0.pco=1024"); } + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t0.pco=1024"); + sendCommand(text); sendCommandAction(63U); } else { char text[50U]; ::sprintf(text, "t2.txt=\"2 %s %s\"", type, talkerAlias); - if (m_screenLayout == 2U) { + if (m_screenLayout & LAYOUT_TA_FONTSIZE) { if (::strlen((char*)talkerAlias) > (16U-4U)) sendCommand("t2.font=3"); if (::strlen((char*)talkerAlias) > (20U-4U)) sendCommand("t2.font=2"); if (::strlen((char*)talkerAlias) > (24U-4U)) sendCommand("t2.font=1"); - - sendCommand("t2.pco=1024"); } + + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t2.pco=1024"); + sendCommand(text); sendCommandAction(71U); } @@ -535,9 +575,11 @@ void CNextion::clearDMRInt(unsigned int slotNo) sendCommand("t0.txt=\"1 Listening\""); sendCommandAction(61U); - if (m_screenLayout == 2U) { - sendCommand("t0.pco=0"); - sendCommand("t0.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t0.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t0.font=4"); } sendCommand("t1.txt=\"\""); @@ -547,9 +589,11 @@ void CNextion::clearDMRInt(unsigned int slotNo) sendCommand("t2.txt=\"2 Listening\""); sendCommandAction(69U); - if (m_screenLayout == 2U) { - sendCommand("t2.pco=0"); - sendCommand("t2.font=4"); + if (m_screenLayout & LAYOUT_TA_ENABLE) { + if (m_screenLayout & LAYOUT_TA_COLOUR) + sendCommand("t2.pco=0"); + if (m_screenLayout & LAYOUT_TA_FONTSIZE) + sendCommand("t2.font=4"); } sendCommand("t3.txt=\"\""); @@ -862,7 +906,7 @@ void CNextion::close() void CNextion::sendCommandAction(unsigned int status) { - if (m_screenLayout<3U) + if (!(m_screenLayout & LAYOUT_DIY)) return; char text[30U]; @@ -881,4 +925,4 @@ void CNextion::sendCommand(const char* command) // we must add a bit of a delay to allow the display to process the commands, else some are getting mangled. // 10 ms is just a guess, but seems to be sufficient. CThread::sleep(10U); - } +} diff --git a/OLED.cpp b/OLED.cpp index 6f9b4ec..02afc2d 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -240,7 +240,8 @@ void COLED::setIdleInt() // m_display.print("Idle"); // m_display.setTextSize(1); - m_display.startscrolldiagright(0x00,0x0f); //the MMDVM logo scrolls the whole screen + if (m_displayScroll && m_displayLogoScreensaver) + m_display.startscrolldiagright(0x00,0x0f); //the MMDVM logo scrolls the whole screen m_display.display(); unsigned char info[100U]; @@ -568,7 +569,8 @@ void COLED::writeCWInt() m_display.setTextSize(1); m_display.display(); - m_display.startscrollright(0x02,0x0f); + if (m_displayScroll) + m_display.startscrollright(0x02,0x0f); } void COLED::clearCWInt() @@ -581,14 +583,16 @@ void COLED::clearCWInt() m_display.setTextSize(1); m_display.display(); - m_display.startscrollleft(0x02,0x0f); + if (m_displayScroll) + m_display.startscrollleft(0x02,0x0f); } void COLED::close() { m_display.clearDisplay(); m_display.fillRect(0, 0, m_display.width(), 16, BLACK); - m_display.startscrollright(0x00,0x01); + if (m_displayScroll) + m_display.startscrollright(0x00,0x01); m_display.setCursor(0,00); m_display.setTextSize(2); m_display.print("-CLOSE-"); diff --git a/UDPSocket.cpp b/UDPSocket.cpp index ba0e35f..2899faf 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2016 by Jonathan Naylor G4KLX + * Copyright (C) 2006-2016,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 @@ -258,3 +258,31 @@ void CUDPSocket::close() ::close(m_fd); #endif } + +unsigned long CUDPSocket::getLocalAddress() const +{ + unsigned long address = 0UL; + + char hostname[80U]; + int ret = ::gethostname(hostname, 80); + if (ret == -1) + return 0UL; + + struct hostent* phe = ::gethostbyname(hostname); + if (phe == NULL) + return 0UL; + + if (phe->h_addrtype != AF_INET) + return 0UL; + + for (unsigned int i = 0U; phe->h_addr_list[i] != NULL; i++) { + struct in_addr addr; + ::memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + if (addr.s_addr != INADDR_LOOPBACK) { + address = addr.s_addr; + break; + } + } + + return address; +} diff --git a/UDPSocket.h b/UDPSocket.h index e0af272..4c21a43 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2011,2013,2015,2016,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 @@ -47,7 +47,9 @@ public: void close(); - static in_addr lookup(const std::string& hostName); + unsigned long getLocalAddress() const; + + static in_addr lookup(const std::string& hostName); private: std::string m_address;