diff --git a/Conf.cpp b/Conf.cpp index 8f2837745..989ccfd9f 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -43,11 +43,13 @@ enum SECTION { SECTION_FUSION, SECTION_P25, SECTION_NXDN, + SECTION_POCSAG, SECTION_DSTAR_NETWORK, SECTION_DMR_NETWORK, SECTION_FUSION_NETWORK, SECTION_P25_NETWORK, SECTION_NXDN_NETWORK, + SECTION_POCSAG_NETWORK, SECTION_TFTSERIAL, SECTION_HD44780, SECTION_NEXTION, @@ -102,6 +104,7 @@ m_modemDMRTXLevel(50.0F), m_modemYSFTXLevel(50.0F), m_modemP25TXLevel(50.0F), m_modemNXDNTXLevel(50.0F), +m_modemPOCSAGTXLevel(50.0F), m_modemRSSIMappingFile(), m_modemTrace(false), m_modemDebug(false), @@ -158,6 +161,7 @@ m_nxdnRAN(1U), m_nxdnSelfOnly(false), m_nxdnRemoteGateway(false), m_nxdnModeHang(10U), +m_pocsagEnabled(false), m_dstarNetworkEnabled(false), m_dstarGatewayAddress(), m_dstarGatewayPort(0U), @@ -195,6 +199,13 @@ m_nxdnLocalAddress(), m_nxdnLocalPort(0U), m_nxdnNetworkModeHang(3U), m_nxdnNetworkDebug(false), +m_pocsagNetworkEnabled(false), +m_pocsagGatewayAddress(), +m_pocsagGatewayPort(0U), +m_pocsagLocalAddress(), +m_pocsagLocalPort(0U), +m_pocsagNetworkModeHang(3U), +m_pocsagNetworkDebug(false), m_tftSerialPort("/dev/ttyAMA0"), m_tftSerialBrightness(50U), m_hd44780Rows(2U), @@ -273,6 +284,8 @@ bool CConf::read() section = SECTION_P25; else if (::strncmp(buffer, "[NXDN]", 6U) == 0) section = SECTION_NXDN; + else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) + section = SECTION_POCSAG; else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) section = SECTION_DSTAR_NETWORK; else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) @@ -283,6 +296,8 @@ bool CConf::read() section = SECTION_P25_NETWORK; else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) section = SECTION_NXDN_NETWORK; + else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) + section = SECTION_POCSAG_NETWORK; else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) section = SECTION_TFTSERIAL; else if (::strncmp(buffer, "[HD44780]", 9U) == 0) @@ -425,6 +440,8 @@ bool CConf::read() m_modemP25TXLevel = float(::atof(value)); else if (::strcmp(key, "NXDNTXLevel") == 0) m_modemNXDNTXLevel = float(::atof(value)); + else if (::strcmp(key, "POCSAGTXLevel") == 0) + m_modemPOCSAGTXLevel = float(::atof(value)); else if (::strcmp(key, "RSSIMappingFile") == 0) m_modemRSSIMappingFile = value; else if (::strcmp(key, "Trace") == 0) @@ -586,6 +603,9 @@ bool CConf::read() m_nxdnRemoteGateway = ::atoi(value) == 1; else if (::strcmp(key, "ModeHang") == 0) m_nxdnModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_POCSAG) { + if (::strcmp(key, "Enable") == 0) + m_pocsagEnabled = ::atoi(value) == 1; } else if (section == SECTION_DSTAR_NETWORK) { if (::strcmp(key, "Enable") == 0) m_dstarNetworkEnabled = ::atoi(value) == 1; @@ -665,6 +685,21 @@ bool CConf::read() m_nxdnNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_nxdnNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_POCSAG_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_pocsagNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_pocsagLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_pocsagLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_pocsagGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_pocsagGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_pocsagNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_pocsagNetworkDebug = ::atoi(value) == 1; } else if (section == SECTION_TFTSERIAL) { if (::strcmp(key, "Port") == 0) m_tftSerialPort = value; @@ -960,6 +995,11 @@ float CConf::getModemNXDNTXLevel() const return m_modemNXDNTXLevel; } +float CConf::getModemPOCSAGTXLevel() const +{ + return m_modemPOCSAGTXLevel; +} + std::string CConf::getModemRSSIMappingFile () const { return m_modemRSSIMappingFile; @@ -1240,6 +1280,11 @@ unsigned int CConf::getNXDNModeHang() const return m_nxdnModeHang; } +bool CConf::getPOCSAGEnabled() const +{ + return m_pocsagEnabled; +} + bool CConf::getDStarNetworkEnabled() const { return m_dstarNetworkEnabled; @@ -1425,6 +1470,41 @@ bool CConf::getNXDNNetworkDebug() const return m_nxdnNetworkDebug; } +bool CConf::getPOCSAGNetworkEnabled() const +{ + return m_pocsagNetworkEnabled; +} + +std::string CConf::getPOCSAGGatewayAddress() const +{ + return m_pocsagGatewayAddress; +} + +unsigned int CConf::getPOCSAGGatewayPort() const +{ + return m_pocsagGatewayPort; +} + +std::string CConf::getPOCSAGLocalAddress() const +{ + return m_pocsagLocalAddress; +} + +unsigned int CConf::getPOCSAGLocalPort() const +{ + return m_pocsagLocalPort; +} + +unsigned int CConf::getPOCSAGNetworkModeHang() const +{ + return m_pocsagNetworkModeHang; +} + +bool CConf::getPOCSAGNetworkDebug() const +{ + return m_pocsagNetworkDebug; +} + std::string CConf::getTFTSerialPort() const { return m_tftSerialPort; diff --git a/Conf.h b/Conf.h index f85c17b44..2ba0a1a8d 100644 --- a/Conf.h +++ b/Conf.h @@ -87,6 +87,7 @@ class CConf float getModemYSFTXLevel() const; float getModemP25TXLevel() const; float getModemNXDNTXLevel() const; + float getModemPOCSAGTXLevel() const; std::string getModemRSSIMappingFile() const; bool getModemTrace() const; bool getModemDebug() const; @@ -158,6 +159,9 @@ class CConf bool getNXDNRemoteGateway() const; unsigned int getNXDNModeHang() const; + // The POCSAG section + bool getPOCSAGEnabled() const; + // The D-Star Network section bool getDStarNetworkEnabled() const; std::string getDStarGatewayAddress() const; @@ -205,6 +209,15 @@ class CConf unsigned int getNXDNNetworkModeHang() const; bool getNXDNNetworkDebug() const; + // The POCSAG Network section + bool getPOCSAGNetworkEnabled() const; + std::string getPOCSAGGatewayAddress() const; + unsigned int getPOCSAGGatewayPort() const; + std::string getPOCSAGLocalAddress() const; + unsigned int getPOCSAGLocalPort() const; + unsigned int getPOCSAGNetworkModeHang() const; + bool getPOCSAGNetworkDebug() const; + // The TFTSERIAL section std::string getTFTSerialPort() const; unsigned int getTFTSerialBrightness() const; @@ -295,6 +308,7 @@ class CConf float m_modemYSFTXLevel; float m_modemP25TXLevel; float m_modemNXDNTXLevel; + float m_modemPOCSAGTXLevel; std::string m_modemRSSIMappingFile; bool m_modemTrace; bool m_modemDebug; @@ -359,6 +373,8 @@ class CConf bool m_nxdnRemoteGateway; unsigned int m_nxdnModeHang; + bool m_pocsagEnabled; + bool m_dstarNetworkEnabled; std::string m_dstarGatewayAddress; unsigned int m_dstarGatewayPort; @@ -401,6 +417,14 @@ class CConf unsigned int m_nxdnNetworkModeHang; bool m_nxdnNetworkDebug; + bool m_pocsagNetworkEnabled; + std::string m_pocsagGatewayAddress; + unsigned int m_pocsagGatewayPort; + std::string m_pocsagLocalAddress; + unsigned int m_pocsagLocalPort; + unsigned int m_pocsagNetworkModeHang; + bool m_pocsagNetworkDebug; + std::string m_tftSerialPort; unsigned int m_tftSerialBrightness; diff --git a/Defines.h b/Defines.h index 1f47836ea..73e9e528f 100644 --- a/Defines.h +++ b/Defines.h @@ -25,6 +25,7 @@ const unsigned char MODE_DMR = 2U; const unsigned char MODE_YSF = 3U; const unsigned char MODE_P25 = 4U; const unsigned char MODE_NXDN = 5U; +const unsigned char MODE_POCSAG = 6U; const unsigned char MODE_CW = 98U; const unsigned char MODE_LOCKOUT = 99U; const unsigned char MODE_ERROR = 100U; diff --git a/MMDVM.ini b/MMDVM.ini index f9d93575a..4fee55064 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -62,6 +62,7 @@ RFLevel=100 # YSFTXLevel=50 # P25TXLevel=50 # NXDNTXLevel=50 +# POCSAGTXLevel=50 RSSIMappingFile=RSSI.dat Trace=0 Debug=0 @@ -127,6 +128,9 @@ SelfOnly=0 RemoteGateway=0 # ModeHang=10 +[POCSAG] +Enable=1 + [D-Star Network] Enable=1 GatewayAddress=127.0.0.1 @@ -174,6 +178,15 @@ GatewayPort=14020 # ModeHang=3 Debug=0 +[POCSAG Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=3800 +GatewayAddress=127.0.0.1 +GatewayPort=4800 +# ModeHang=3 +Debug=0 + [TFT Serial] # Port=modem Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index d3ef606fb..ff7cf00fa 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -30,6 +30,7 @@ #include "YSFControl.h" #include "P25Control.h" #include "NXDNControl.h" +#include "POCSAGControl.h" #include "Nextion.h" #include "LCDproc.h" #include "Thread.h" @@ -134,6 +135,7 @@ m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), +m_pocsagNetwork(NULL), m_display(NULL), m_ump(NULL), m_mode(MODE_IDLE), @@ -147,6 +149,7 @@ m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), +m_pocsagNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), @@ -157,6 +160,7 @@ m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_pocsagEnabled(false), m_cwIdTime(0U), m_dmrLookup(NULL), m_nxdnLookup(NULL), @@ -308,6 +312,12 @@ int CMMDVMHost::run() return 1; } + if (m_pocsagEnabled && m_conf.getPOCSAGNetworkEnabled()) { + ret = createPOCSAGNetwork(); + if (!ret) + return 1; + } + in_addr transparentAddress; unsigned int transparentPort = 0U; CUDPSocket* transparentSocket = NULL; @@ -545,6 +555,10 @@ int CMMDVMHost::run() nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_display, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi); } + CPOCSAGControl* pocsag = NULL; + if (m_pocsagEnabled) + pocsag = new CPOCSAGControl(m_pocsagNetwork, m_display); + setMode(MODE_IDLE); LogMessage("MMDVMHost-%s is running", VERSION); @@ -843,6 +857,25 @@ int CMMDVMHost::run() } } + if (pocsag != NULL) { + ret = m_modem->hasPOCSAGSpace(); + if (ret) { + len = pocsag->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_pocsagNetModeHang); + setMode(MODE_POCSAG); + } + if (m_mode == MODE_POCSAG) { + m_modem->writePOCSAGData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("POCSAG data received when in mode %u", m_mode); + } + } + } + } + if (transparentSocket != NULL) { in_addr address; unsigned int port = 0U; @@ -869,6 +902,8 @@ int CMMDVMHost::run() p25->clock(ms); if (nxdn != NULL) nxdn->clock(ms); + if (pocsag != NULL) + pocsag->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); @@ -880,6 +915,8 @@ int CMMDVMHost::run() m_p25Network->clock(ms); if (m_nxdnNetwork != NULL) m_nxdnNetwork->clock(ms); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { @@ -965,6 +1002,11 @@ int CMMDVMHost::run() delete m_nxdnNetwork; } + if (m_pocsagNetwork != NULL) { + m_pocsagNetwork->close(); + delete m_pocsagNetwork; + } + if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; @@ -975,6 +1017,7 @@ int CMMDVMHost::run() delete ysf; delete p25; delete nxdn; + delete pocsag; return 0; } @@ -994,6 +1037,7 @@ bool CMMDVMHost::createModem() float ysfTXLevel = m_conf.getModemYSFTXLevel(); float p25TXLevel = m_conf.getModemP25TXLevel(); float nxdnTXLevel = m_conf.getModemNXDNTXLevel(); + float pocsagTXLevel = m_conf.getModemPOCSAGTXLevel(); bool trace = m_conf.getModemTrace(); bool debug = m_conf.getModemDebug(); unsigned int colorCode = m_conf.getDMRColorCode(); @@ -1026,12 +1070,13 @@ bool CMMDVMHost::createModem() LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel); LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); + LogInfo(" POCSAG TX Level: %.1f%%", pocsagTXLevel); LogInfo(" RX Frequency: %uHz (%uHz)", rxFrequency, rxFrequency + rxOffset); LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug); - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled); - m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled); + m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel); m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel); m_modem->setDMRParams(colorCode); m_modem->setYSFParams(lowDeviation, txHang); @@ -1233,17 +1278,48 @@ bool CMMDVMHost::createNXDNNetwork() return true; } +bool CMMDVMHost::createPOCSAGNetwork() +{ + std::string gatewayAddress = m_conf.getPOCSAGGatewayAddress(); + unsigned int gatewayPort = m_conf.getPOCSAGGatewayPort(); + std::string localAddress = m_conf.getPOCSAGLocalAddress(); + unsigned int localPort = m_conf.getPOCSAGLocalPort(); + m_pocsagNetModeHang = m_conf.getPOCSAGNetworkModeHang(); + bool debug = m_conf.getPOCSAGNetworkDebug(); + + LogInfo("POCSAG Network Parameters"); + 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_pocsagNetModeHang); + + m_pocsagNetwork = new CPOCSAGNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + + bool ret = m_pocsagNetwork->open(); + if (!ret) { + delete m_pocsagNetwork; + m_pocsagNetwork = NULL; + return false; + } + + m_pocsagNetwork->enable(true); + + return true; +} + void CMMDVMHost::readParams() { - m_dstarEnabled = m_conf.getDStarEnabled(); - m_dmrEnabled = m_conf.getDMREnabled(); - m_ysfEnabled = m_conf.getFusionEnabled(); - m_p25Enabled = m_conf.getP25Enabled(); - m_nxdnEnabled = m_conf.getNXDNEnabled(); - m_duplex = m_conf.getDuplex(); - m_callsign = m_conf.getCallsign(); - m_id = m_conf.getId(); - m_timeout = m_conf.getTimeout(); + m_dstarEnabled = m_conf.getDStarEnabled(); + m_dmrEnabled = m_conf.getDMREnabled(); + m_ysfEnabled = m_conf.getFusionEnabled(); + m_p25Enabled = m_conf.getP25Enabled(); + m_nxdnEnabled = m_conf.getNXDNEnabled(); + m_pocsagEnabled = m_conf.getPOCSAGEnabled(); + m_duplex = m_conf.getDuplex(); + m_callsign = m_conf.getCallsign(); + m_id = m_conf.getId(); + m_timeout = m_conf.getTimeout(); LogInfo("General Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); @@ -1255,6 +1331,7 @@ void CMMDVMHost::readParams() LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled"); LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); + LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled"); } void CMMDVMHost::createDisplay() @@ -1426,6 +1503,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_DSTAR); if (m_ump != NULL) m_ump->setMode(MODE_DSTAR); @@ -1443,6 +1522,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_DMR); if (m_ump != NULL) m_ump->setMode(MODE_DMR); @@ -1464,6 +1545,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_YSF); if (m_ump != NULL) m_ump->setMode(MODE_YSF); @@ -1481,6 +1564,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_ysfNetwork->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_P25); if (m_ump != NULL) m_ump->setMode(MODE_P25); @@ -1498,6 +1583,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_ysfNetwork->enable(false); if (m_p25Network != NULL) m_p25Network->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_NXDN); if (m_ump != NULL) m_ump->setMode(MODE_NXDN); @@ -1506,6 +1593,25 @@ void CMMDVMHost::setMode(unsigned char mode) m_cwIdTimer.stop(); break; + case MODE_POCSAG: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(false); + m_modem->setMode(MODE_POCSAG); + if (m_ump != NULL) + m_ump->setMode(MODE_POCSAG); + m_mode = MODE_POCSAG; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + case MODE_LOCKOUT: LogMessage("Mode set to Lockout"); if (m_dstarNetwork != NULL) @@ -1518,6 +1624,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1543,6 +1651,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1566,6 +1676,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(true); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(true); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); diff --git a/MMDVMHost.h b/MMDVMHost.h index 787df3983..c19623ff0 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -19,6 +19,7 @@ #if !defined(MMDVMHOST_H) #define MMDVMHOST_H +#include "POCSAGNetwork.h" #include "DStarNetwork.h" #include "NXDNNetwork.h" #include "NXDNLookup.h" @@ -43,42 +44,45 @@ class CMMDVMHost int run(); private: - CConf m_conf; - CModem* m_modem; - CDStarNetwork* m_dstarNetwork; - CDMRNetwork* m_dmrNetwork; - CYSFNetwork* m_ysfNetwork; - CP25Network* m_p25Network; - CNXDNNetwork* m_nxdnNetwork; - CDisplay* m_display; - CUMP* m_ump; - unsigned char m_mode; - unsigned int m_dstarRFModeHang; - unsigned int m_dmrRFModeHang; - unsigned int m_ysfRFModeHang; - unsigned int m_p25RFModeHang; - unsigned int m_nxdnRFModeHang; - unsigned int m_dstarNetModeHang; - unsigned int m_dmrNetModeHang; - unsigned int m_ysfNetModeHang; - unsigned int m_p25NetModeHang; - unsigned int m_nxdnNetModeHang; - CTimer m_modeTimer; - CTimer m_dmrTXTimer; - CTimer m_cwIdTimer; - bool m_duplex; - unsigned int m_timeout; - bool m_dstarEnabled; - bool m_dmrEnabled; - bool m_ysfEnabled; - bool m_p25Enabled; - bool m_nxdnEnabled; - unsigned int m_cwIdTime; - CDMRLookup* m_dmrLookup; - CNXDNLookup* m_nxdnLookup; - std::string m_callsign; - unsigned int m_id; - std::string m_cwCallsign; + CConf m_conf; + CModem* m_modem; + CDStarNetwork* m_dstarNetwork; + CDMRNetwork* m_dmrNetwork; + CYSFNetwork* m_ysfNetwork; + CP25Network* m_p25Network; + CNXDNNetwork* m_nxdnNetwork; + CPOCSAGNetwork* m_pocsagNetwork; + CDisplay* m_display; + CUMP* m_ump; + unsigned char m_mode; + unsigned int m_dstarRFModeHang; + unsigned int m_dmrRFModeHang; + unsigned int m_ysfRFModeHang; + unsigned int m_p25RFModeHang; + unsigned int m_nxdnRFModeHang; + unsigned int m_dstarNetModeHang; + unsigned int m_dmrNetModeHang; + unsigned int m_ysfNetModeHang; + unsigned int m_p25NetModeHang; + unsigned int m_nxdnNetModeHang; + unsigned int m_pocsagNetModeHang; + CTimer m_modeTimer; + CTimer m_dmrTXTimer; + CTimer m_cwIdTimer; + bool m_duplex; + unsigned int m_timeout; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + bool m_p25Enabled; + bool m_nxdnEnabled; + bool m_pocsagEnabled; + unsigned int m_cwIdTime; + CDMRLookup* m_dmrLookup; + CNXDNLookup* m_nxdnLookup; + std::string m_callsign; + unsigned int m_id; + std::string m_cwCallsign; void readParams(); bool createModem(); @@ -87,6 +91,7 @@ class CMMDVMHost bool createYSFNetwork(); bool createP25Network(); bool createNXDNNetwork(); + bool createPOCSAGNetwork(); void createDisplay(); void setMode(unsigned char mode); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index e63859cc8..e967e9dfd 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -212,6 +212,9 @@ + + + @@ -294,6 +297,8 @@ + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index c276fc7ef..20a3aad08 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -263,6 +263,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -493,5 +502,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index ae0175b52..8e0591570 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ + UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi b/Makefile.Pi index 3ef95137a..0d8475b70 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ + UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index ec8042702..27266def2 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \ Nextion.o NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index e35f8d3bc..602ad3bb8 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \ Nextion.o NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 600e7db34..6ef3fd8e7 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \ Nextion.o NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 4fbc2a84c..0652e3476 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \ Nextion.o NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Solaris b/Makefile.Solaris index 8f58787b8..a42b0445e 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ NullDisplay.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ + UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Modem.cpp b/Modem.cpp index 037935bd5..8db90122c 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -21,6 +21,7 @@ #include "YSFDefines.h" #include "P25Defines.h" #include "NXDNDefines.h" +#include "POCSAGDefines.h" #include "Thread.h" #include "Modem.h" #include "Utils.h" @@ -71,6 +72,8 @@ const unsigned char MMDVM_P25_LOST = 0x32U; const unsigned char MMDVM_NXDN_DATA = 0x40U; const unsigned char MMDVM_NXDN_LOST = 0x41U; +const unsigned char MMDVM_POCSAG_DATA = 0x50U; + const unsigned char MMDVM_ACK = 0x70U; const unsigned char MMDVM_NAK = 0x7FU; @@ -107,6 +110,7 @@ m_dmrTXLevel(0U), m_ysfTXLevel(0U), m_p25TXLevel(0U), m_nxdnTXLevel(0U), +m_pocsagTXLevel(0U), m_trace(trace), m_debug(debug), m_rxFrequency(0U), @@ -116,6 +120,7 @@ m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_pocsagEnabled(false), m_rxDCOffset(0), m_txDCOffset(0), m_serial(port, SERIAL_115200, true), @@ -134,6 +139,7 @@ m_rxP25Data(1000U, "Modem RX P25"), m_txP25Data(1000U, "Modem TX P25"), m_rxNXDNData(1000U, "Modem RX NXDN"), m_txNXDNData(1000U, "Modem TX NXDN"), +m_txPOCSAGData(1000U, "Modem TX POCSAG"), m_rxTransparentData(1000U, "Modem RX Transparent"), m_txTransparentData(1000U, "Modem TX Transparent"), m_statusTimer(1000U, 0U, 250U), @@ -145,6 +151,7 @@ m_dmrSpace2(0U), m_ysfSpace(0U), m_p25Space(0U), m_nxdnSpace(0U), +m_pocsagSpace(0U), m_tx(false), m_cd(false), m_lockout(false), @@ -170,24 +177,26 @@ void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int tx m_rfLevel = rfLevel; } -void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled) +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled) { - m_dstarEnabled = dstarEnabled; - m_dmrEnabled = dmrEnabled; - m_ysfEnabled = ysfEnabled; - m_p25Enabled = p25Enabled; - m_nxdnEnabled = nxdnEnabled; + m_dstarEnabled = dstarEnabled; + m_dmrEnabled = dmrEnabled; + m_ysfEnabled = ysfEnabled; + m_p25Enabled = p25Enabled; + m_nxdnEnabled = nxdnEnabled; + m_pocsagEnabled = pocsagEnabled; } -void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel) +void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel) { - m_rxLevel = rxLevel; - m_cwIdTXLevel = cwIdTXLevel; - m_dstarTXLevel = dstarTXLevel; - m_dmrTXLevel = dmrTXLevel; - m_ysfTXLevel = ysfTXLevel; - m_p25TXLevel = p25TXLevel; - m_nxdnTXLevel = nxdnTXLevel; + m_rxLevel = rxLevel; + m_cwIdTXLevel = cwIdTXLevel; + m_dstarTXLevel = dstarTXLevel; + m_dmrTXLevel = dmrTXLevel; + m_ysfTXLevel = ysfTXLevel; + m_p25TXLevel = p25TXLevel; + m_nxdnTXLevel = nxdnTXLevel; + m_pocsagTXLevel = pocsagTXLevel; } void CModem::setDMRParams(unsigned int colorCode) @@ -498,15 +507,16 @@ void CModem::clock(unsigned int ms) m_cd = (m_buffer[5U] & 0x40U) == 0x40U; - m_dstarSpace = m_buffer[6U]; - m_dmrSpace1 = m_buffer[7U]; - m_dmrSpace2 = m_buffer[8U]; - m_ysfSpace = m_buffer[9U]; - m_p25Space = m_buffer[10U]; - m_nxdnSpace = m_buffer[11U]; + m_dstarSpace = m_buffer[6U]; + m_dmrSpace1 = m_buffer[7U]; + m_dmrSpace2 = m_buffer[8U]; + m_ysfSpace = m_buffer[9U]; + m_p25Space = m_buffer[10U]; + m_nxdnSpace = m_buffer[11U]; + m_pocsagSpace = m_buffer[12U]; m_inactivityTimer.start(); - // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, int(m_lockout), int(m_cd)); + // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, int(m_lockout), int(m_cd)); } break; @@ -676,6 +686,23 @@ void CModem::clock(unsigned int ms) m_nxdnSpace--; } + if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) { + unsigned char len = 0U; + m_txPOCSAGData.getData(&len, 1U); + m_txPOCSAGData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX POCSAG Data", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing POCSAG data to the MMDVM"); + + m_playoutTimer.start(); + + m_pocsagSpace--; + } + if (!m_txTransparentData.isEmpty()) { unsigned char len = 0U; m_txTransparentData.getData(&len, 1U); @@ -995,6 +1022,33 @@ bool CModem::writeNXDNData(const unsigned char* data, unsigned int length) return true; } +bool CModem::hasPOCSAGSpace() const +{ + unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_POCSAG_DATA; + + ::memcpy(buffer + 3U, data, length); + + unsigned char len = length + 3U; // XXX Check lengths + m_txPOCSAGData.addData(&len, 1U); + m_txPOCSAGData.addData(buffer, len); + + return true; +} + bool CModem::writeTransparentData(const unsigned char* data, unsigned int length) { assert(data != NULL); @@ -1123,11 +1177,11 @@ bool CModem::readStatus() bool CModem::setConfig() { - unsigned char buffer[20U]; + unsigned char buffer[30U]; buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 20U; + buffer[1U] = 21U; buffer[2U] = MMDVM_SET_CONFIG; @@ -1156,6 +1210,8 @@ bool CModem::setConfig() buffer[4U] |= 0x08U; if (m_nxdnEnabled) buffer[4U] |= 0x10U; + if (m_pocsagEnabled) + buffer[4U] |= 0x20U; buffer[5U] = m_txDelay / 10U; // In 10ms units @@ -1183,10 +1239,12 @@ bool CModem::setConfig() buffer[19U] = (unsigned char)m_ysfTXHang; - // CUtils::dump(1U, "Written", buffer, 20U); + buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); + + // CUtils::dump(1U, "Written", buffer, 21U); - int ret = m_serial.write(buffer, 20U); - if (ret != 20) + int ret = m_serial.write(buffer, 21U); + if (ret != 21) return false; unsigned int count = 0U; diff --git a/Modem.h b/Modem.h index 37ef76fc2..2e32cb16e 100644 --- a/Modem.h +++ b/Modem.h @@ -38,8 +38,8 @@ class CModem { ~CModem(); void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel); - void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled); - void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel); + void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled); + void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel); void setDMRParams(unsigned int colorCode); void setYSFParams(bool loDev, unsigned int txHang); @@ -61,6 +61,7 @@ class CModem { bool hasYSFSpace() const; bool hasP25Space() const; bool hasNXDNSpace() const; + bool hasPOCSAGSpace() const; bool hasTX() const; bool hasCD() const; @@ -74,6 +75,7 @@ class CModem { bool writeYSFData(const unsigned char* data, unsigned int length); bool writeP25Data(const unsigned char* data, unsigned int length); bool writeNXDNData(const unsigned char* data, unsigned int length); + bool writePOCSAGData(const unsigned char* data, unsigned int length); bool writeTransparentData(const unsigned char* data, unsigned int length); bool writeDMRStart(bool tx); @@ -110,6 +112,7 @@ class CModem { float m_ysfTXLevel; float m_p25TXLevel; float m_nxdnTXLevel; + float m_pocsagTXLevel; float m_rfLevel; bool m_trace; bool m_debug; @@ -120,6 +123,7 @@ class CModem { bool m_ysfEnabled; bool m_p25Enabled; bool m_nxdnEnabled; + bool m_pocsagEnabled; int m_rxDCOffset; int m_txDCOffset; CSerialController m_serial; @@ -138,6 +142,7 @@ class CModem { CRingBuffer m_txP25Data; CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; + CRingBuffer m_txPOCSAGData; CRingBuffer m_rxTransparentData; CRingBuffer m_txTransparentData; CTimer m_statusTimer; @@ -149,6 +154,7 @@ class CModem { unsigned int m_ysfSpace; unsigned int m_p25Space; unsigned int m_nxdnSpace; + unsigned int m_pocsagSpace; bool m_tx; bool m_cd; bool m_lockout; diff --git a/POCSAGControl.cpp b/POCSAGControl.cpp new file mode 100644 index 000000000..216162339 --- /dev/null +++ b/POCSAGControl.cpp @@ -0,0 +1,112 @@ +/* +* Copyright (C) 2018 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; version 2 of the License. +* +* 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. +*/ + +#include "POCSAGControl.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include +#include + +// #define DUMP_POCSAG + +const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#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]) + +CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display) : +m_network(network), +m_display(display), +m_queue(5000U, "POCSAG Control"), +m_fp(NULL) +{ + assert(display != NULL); +} + +CPOCSAGControl::~CPOCSAGControl() +{ +} + +unsigned int CPOCSAGControl::readModem(unsigned char* data) +{ + assert(data != NULL); + + if (m_queue.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_queue.getData(&len, 1U); + + m_queue.getData(data, len); + + return len; +} + +void CPOCSAGControl::writeNetwork() +{ + unsigned char netData[40U]; + unsigned int length = m_network->read(netData); + if (length == 0U) + return; + +} + +void CPOCSAGControl::clock(unsigned int ms) +{ + if (m_network != NULL) + writeNetwork(); + +} + +bool CPOCSAGControl::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "POCSAG_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("POCSAG", 1U, 3U, m_fp); + + return true; +} + +bool CPOCSAGControl::writeFile(const unsigned char* data) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, POCSAG_FRAME_LENGTH_BYTES, m_fp); + + return true; +} + +void CPOCSAGControl::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} diff --git a/POCSAGControl.h b/POCSAGControl.h new file mode 100644 index 000000000..aa580ef6f --- /dev/null +++ b/POCSAGControl.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2018 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. +*/ + +#if !defined(POCSAGControl_H) +#define POCSAGControl_H + +#include "POCSAGNetwork.h" +#include "POCSAGDefines.h" +#include "RingBuffer.h" +#include "Display.h" +#include "Defines.h" + +#include + +class CPOCSAGControl { +public: + CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display); + ~CPOCSAGControl(); + + unsigned int readModem(unsigned char* data); + + void clock(unsigned int ms); + +private: + CPOCSAGNetwork* m_network; + CDisplay* m_display; + CRingBuffer m_queue; + unsigned int m_frames; + FILE* m_fp; + + void writeNetwork(); + + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); +}; + +#endif diff --git a/POCSAGDefines.h b/POCSAGDefines.h new file mode 100644 index 000000000..1919226da --- /dev/null +++ b/POCSAGDefines.h @@ -0,0 +1,33 @@ +/* +* Copyright (C) 2018 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. +*/ + +#if !defined(POCSAGDEFINES_H) +#define POCSAGDEFINES_H + +#include + +const unsigned int POCSAG_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate + +const unsigned int POCSAG_FRAME_LENGTH_WORDS = 17U; +const unsigned int POCSAG_FRAME_LENGTH_BYTES = POCSAG_FRAME_LENGTH_WORDS * sizeof(uint32_t); + +const uint32_t POCSAG_SYNC_WORD = 0x7CD215D8U; + +const uint32_t POCSAG_IDLE_WORD = 0x7A89C197U; + +#endif diff --git a/POCSAGNetwork.cpp b/POCSAGNetwork.cpp new file mode 100644 index 000000000..5716a248a --- /dev/null +++ b/POCSAGNetwork.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018 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 "POCSAGDefines.h" +#include "POCSAGNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CPOCSAGNetwork::CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : +m_socket(myAddress, myPort), +m_address(), +m_port(gatewayPort), +m_debug(debug), +m_enabled(false), +m_buffer(1000U, "POCSAG Network") +{ + m_address = CUDPSocket::lookup(gatewayAddress); +} + +CPOCSAGNetwork::~CPOCSAGNetwork() +{ +} + +bool CPOCSAGNetwork::open() +{ + LogMessage("Opening POCSAG network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +void CPOCSAGNetwork::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 || m_port != port) { + LogMessage("POCSAG 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, "POCSAG", 6U) != 0) + return; + + if (!m_enabled) + return; + + if (m_debug) + CUtils::dump(1U, "POCSAG Network Data Received", buffer, length); + + unsigned char len = length - 6U; + m_buffer.addData(&len, 1U); + m_buffer.addData(buffer + 6U, length - 6U); +} + +unsigned int CPOCSAGNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_buffer.getData(&len, 1U); + m_buffer.getData(data, len); + + return len; +} + +void CPOCSAGNetwork::reset() +{ +} + +void CPOCSAGNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing POCSAG network connection"); +} + +void CPOCSAGNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + + unsigned char c = enabled ? 0xFFU : 0x00U; + + m_socket.write(&c, 1U, m_address, m_port); + + m_enabled = enabled; +} diff --git a/POCSAGNetwork.h b/POCSAGNetwork.h new file mode 100644 index 000000000..fe9520358 --- /dev/null +++ b/POCSAGNetwork.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 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 POCSAGNetwork_H +#define POCSAGNetwork_H + +#include "POCSAGDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CPOCSAGNetwork { +public: + CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CPOCSAGNetwork(); + + bool open(); + + void enable(bool enabled); + + unsigned int read(unsigned char* data); + + void reset(); + + void close(); + + 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/UMP/UMP.ino b/UMP/UMP.ino index d35d18f16..4cf98e301 100644 --- a/UMP/UMP.ino +++ b/UMP/UMP.ino @@ -29,6 +29,7 @@ #define PIN_YSF 5 #define PIN_P25 6 #define PIN_NXDN 7 +#define PIN_POCSAG 8 #define PIN_TX 10 #define PIN_CD 11 @@ -64,17 +65,19 @@ void setup() pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); pinMode(PIN_NXDN, OUTPUT); + pinMode(PIN_POCSAG, OUTPUT); pinMode(PIN_TX, OUTPUT); pinMode(PIN_CD, OUTPUT); pinMode(PIN_LOCKOUT, INPUT); - digitalWrite(PIN_DSTAR, LOW); - digitalWrite(PIN_DMR, LOW); - digitalWrite(PIN_YSF, LOW); - digitalWrite(PIN_P25, LOW); - digitalWrite(PIN_NXDN, LOW); - digitalWrite(PIN_TX, LOW); - digitalWrite(PIN_CD, LOW); + digitalWrite(PIN_DSTAR, LOW); + digitalWrite(PIN_DMR, LOW); + digitalWrite(PIN_YSF, LOW); + digitalWrite(PIN_P25, LOW); + digitalWrite(PIN_NXDN, LOW); + digitalWrite(PIN_POCSAG, LOW); + digitalWrite(PIN_TX, LOW); + digitalWrite(PIN_CD, LOW); } #define UMP_FRAME_START 0xF0U @@ -95,6 +98,7 @@ void setup() #define MODE_YSF 3U #define MODE_P25 4U #define MODE_NXDN 5U +#define MODE_POCSAG 6U bool m_started = false; uint32_t m_count = 0U; @@ -129,11 +133,12 @@ void loop() m_started = true; break; case UMP_SET_MODE: - digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW); - digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW); - digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW); - digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW); - digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW); + digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW); + digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW); + digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW); + digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW); + digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW); + digitalWrite(PIN_POCSAG, m_buffer[3U] == MODE_POCSAG ? HIGH : LOW); break; case UMP_SET_TX: digitalWrite(PIN_TX, m_buffer[3U] == 0x01U ? HIGH : LOW);