diff --git a/Conf.cpp b/Conf.cpp index 8c07bc6..071c717 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -51,6 +51,7 @@ enum SECTION { SECTION_P25_NETWORK, SECTION_NXDN_NETWORK, SECTION_POCSAG_NETWORK, + SECTION_FM_NETWORK, SECTION_TFTSERIAL, SECTION_HD44780, SECTION_NEXTION, @@ -246,6 +247,13 @@ m_pocsagLocalAddress(), m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), +m_fmNetworkEnabled(false), +m_fmGatewayAddress(), +m_fmGatewayPort(0U), +m_fmLocalAddress(), +m_fmLocalPort(0U), +m_fmNetworkModeHang(3U), +m_fmNetworkDebug(false), m_tftSerialPort("/dev/ttyAMA0"), m_tftSerialBrightness(50U), m_hd44780Rows(2U), @@ -351,6 +359,8 @@ bool CConf::read() section = SECTION_NXDN_NETWORK; else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) section = SECTION_POCSAG_NETWORK; + else if (::strncmp(buffer, "[FM Network]", 12U) == 0) + section = SECTION_FM_NETWORK; else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) section = SECTION_TFTSERIAL; else if (::strncmp(buffer, "[HD44780]", 9U) == 0) @@ -858,6 +868,21 @@ bool CConf::read() m_pocsagNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_pocsagNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_POCSAG_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_fmNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_fmLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_fmLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_fmGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_fmGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fmNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fmNetworkDebug = ::atoi(value) == 1; } else if (section == SECTION_TFTSERIAL) { if (::strcmp(key, "Port") == 0) m_tftSerialPort = value; @@ -1871,6 +1896,41 @@ bool CConf::getPOCSAGNetworkDebug() const return m_pocsagNetworkDebug; } +bool CConf::getFMNetworkEnabled() const +{ + return m_fmNetworkEnabled; +} + +std::string CConf::getFMGatewayAddress() const +{ + return m_fmGatewayAddress; +} + +unsigned int CConf::getFMGatewayPort() const +{ + return m_fmGatewayPort; +} + +std::string CConf::getFMLocalAddress() const +{ + return m_fmLocalAddress; +} + +unsigned int CConf::getFMLocalPort() const +{ + return m_fmLocalPort; +} + +unsigned int CConf::getFMNetworkModeHang() const +{ + return m_fmNetworkModeHang; +} + +bool CConf::getFMNetworkDebug() const +{ + return m_fmNetworkDebug; +} + std::string CConf::getTFTSerialPort() const { return m_tftSerialPort; diff --git a/Conf.h b/Conf.h index f5a40bd..915a21c 100644 --- a/Conf.h +++ b/Conf.h @@ -257,6 +257,15 @@ public: unsigned int getPOCSAGNetworkModeHang() const; bool getPOCSAGNetworkDebug() const; + // The FM Network section + bool getFMNetworkEnabled() const; + std::string getFMGatewayAddress() const; + unsigned int getFMGatewayPort() const; + std::string getFMLocalAddress() const; + unsigned int getFMLocalPort() const; + unsigned int getFMNetworkModeHang() const; + bool getFMNetworkDebug() const; + // The TFTSERIAL section std::string getTFTSerialPort() const; unsigned int getTFTSerialBrightness() const; @@ -518,6 +527,14 @@ private: unsigned int m_pocsagNetworkModeHang; bool m_pocsagNetworkDebug; + bool m_fmNetworkEnabled; + std::string m_fmGatewayAddress; + unsigned int m_fmGatewayPort; + std::string m_fmLocalAddress; + unsigned int m_fmLocalPort; + unsigned int m_fmNetworkModeHang; + bool m_fmNetworkDebug; + std::string m_tftSerialPort; unsigned int m_tftSerialBrightness; diff --git a/FMControl.h b/FMControl.h new file mode 100644 index 0000000..57dfd0b --- /dev/null +++ b/FMControl.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015-2019 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(FMControl_H) +#define FMControl_H + +#include "FMNetwork.h" +#include "RingBuffer.h" +#include "StopWatch.h" +#include "Defines.h" +#include "Timer.h" +#include "Modem.h" + +#include + +class CFMControl { +public: + CFMControl(CFMNetwork* network); + ~CFMControl(); + + bool writeModem(unsigned char* data, unsigned int len); + + unsigned int readModem(unsigned char* data, unsigned int space); + + void clock(unsigned int ms); + + void enable(bool enabled); + +private: + CFMNetwork* m_network; + CRingBuffer m_queue; + bool m_enabled; + FILE* m_fp; + + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); +}; + +#endif diff --git a/FMNetwork.h b/FMNetwork.h new file mode 100644 index 0000000..5229098 --- /dev/null +++ b/FMNetwork.h @@ -0,0 +1,55 @@ +/* + * 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 + * 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 FMNetwork_H +#define FMNetwork_H + +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CFMNetwork { +public: + CFMNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CFMNetwork(); + + 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/MMDVM.ini b/MMDVM.ini index 5e71a84..a86bdd3 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -227,6 +227,15 @@ GatewayPort=4800 # ModeHang=3 Debug=0 +[FM Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=3810 +GatewayAddress=127.0.0.1 +GatewayPort=4810 +# ModeHang=3 +Debug=0 + [TFT Serial] # Port=modem Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index e2a7766..b9f866e 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -119,12 +119,14 @@ m_ysf(NULL), m_p25(NULL), m_nxdn(NULL), m_pocsag(NULL), +m_fm(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), m_pocsagNetwork(NULL), +m_fmNetwork(NULL), m_display(NULL), m_ump(NULL), m_mode(MODE_IDLE), @@ -139,6 +141,7 @@ m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), m_pocsagNetModeHang(3U), +m_fmNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), @@ -317,6 +320,12 @@ int CMMDVMHost::run() return 1; } + if (m_fmEnabled && m_conf.getFMNetworkEnabled()) { + ret = createFMNetwork(); + if (!ret) + return 1; + } + in_addr transparentAddress; unsigned int transparentPort = 0U; CUDPSocket* transparentSocket = NULL; @@ -606,6 +615,9 @@ int CMMDVMHost::run() pocsagTimer.start(); } + if (m_fmEnabled) + m_fm = new CFMControl(m_fmNetwork); + bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { unsigned int port = m_conf.getRemoteControlPort(); @@ -798,6 +810,22 @@ int CMMDVMHost::run() } } + len = m_modem->readFMData(data); + if (m_nxdn != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = m_fm->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_nxdnRFModeHang); + setMode(MODE_FM); + } + } else if (m_mode == MODE_FM) { + m_fm->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM modem data received when in mode %u", m_mode); + } + } + len = m_modem->readTransparentData(data); if (transparentSocket != NULL && len > 0U) transparentSocket->write(data, len, transparentAddress, transparentPort); @@ -948,6 +976,25 @@ int CMMDVMHost::run() } } + if (m_fm != NULL) { + unsigned int space = m_modem->getFMSpace(); + if (space > 0U) { + len = m_fm->readModem(data, space); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_fmNetModeHang); + setMode(MODE_FM); + } + if (m_mode == MODE_FM) { + m_modem->writeFMData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM data received when in mode %u", m_mode); + } + } + } + } + if (transparentSocket != NULL) { in_addr address; unsigned int port = 0U; @@ -980,6 +1027,8 @@ int CMMDVMHost::run() m_nxdn->clock(ms); if (m_pocsag != NULL) m_pocsag->clock(ms); + if (m_fm != NULL) + m_fm->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); @@ -993,6 +1042,8 @@ int CMMDVMHost::run() m_nxdnNetwork->clock(ms); if (m_pocsagNetwork != NULL) m_pocsagNetwork->clock(ms); + if (m_fmNetwork != NULL) + m_fmNetwork->clock(ms); if (m_mobileGPS != NULL) m_mobileGPS->clock(ms); @@ -1118,6 +1169,11 @@ int CMMDVMHost::run() delete m_pocsagNetwork; } + if (m_fmNetwork != NULL) { + m_fmNetwork->close(); + delete m_fmNetwork; + } + if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; @@ -1134,6 +1190,7 @@ int CMMDVMHost::run() delete m_p25; delete m_nxdn; delete m_pocsag; + delete m_fm; return 0; } @@ -1517,6 +1574,36 @@ bool CMMDVMHost::createPOCSAGNetwork() return true; } +bool CMMDVMHost::createFMNetwork() +{ + std::string gatewayAddress = m_conf.getFMGatewayAddress(); + unsigned int gatewayPort = m_conf.getFMGatewayPort(); + std::string localAddress = m_conf.getFMLocalAddress(); + unsigned int localPort = m_conf.getFMLocalPort(); + m_fmNetModeHang = m_conf.getFMNetworkModeHang(); + bool debug = m_conf.getFMNetworkDebug(); + + LogInfo("FM 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_fmNetModeHang); + + m_fmNetwork = new CFMNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + + bool ret = m_fmNetwork->open(); + if (!ret) { + delete m_fmNetwork; + m_fmNetwork = NULL; + return false; + } + + m_fmNetwork->enable(true); + + return true; +} + void CMMDVMHost::readParams() { m_dstarEnabled = m_conf.getDStarEnabled(); @@ -1564,6 +1651,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(true); if (m_dmr != NULL) @@ -1598,6 +1687,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1636,6 +1727,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1670,6 +1763,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1704,6 +1799,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1738,6 +1835,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1812,6 +1911,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1852,6 +1953,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1890,6 +1993,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(true); if (m_dstar != NULL) m_dstar->enable(true); if (m_dmr != NULL) @@ -1980,55 +2085,55 @@ void CMMDVMHost::remoteControl() processModeCommand(MODE_NXDN, m_nxdnRFModeHang); break; case RCD_MODE_FM: - if (m_fmEnabled != false) + if (m_fmEnabled) processModeCommand(MODE_FM, 0); break; case RCD_ENABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==false) + if (m_dstar != NULL && !m_dstarEnabled) processEnableCommand(m_dstarEnabled, true); break; case RCD_ENABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==false) + if (m_dmr != NULL && !m_dmrEnabled) processEnableCommand(m_dmrEnabled, true); break; case RCD_ENABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==false) + if (m_ysf != NULL && !m_ysfEnabled) processEnableCommand(m_ysfEnabled, true); break; case RCD_ENABLE_P25: - if (m_p25 != NULL && m_p25Enabled==false) + if (m_p25 != NULL && !m_p25Enabled) processEnableCommand(m_p25Enabled, true); break; case RCD_ENABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==false) + if (m_nxdn != NULL && !m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, true); break; case RCD_ENABLE_FM: - if (m_fmEnabled==false) + if (!m_fmEnabled) processEnableCommand(m_fmEnabled, true); break; case RCD_DISABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==true) + if (m_dstar != NULL && m_dstarEnabled) processEnableCommand(m_dstarEnabled, false); break; case RCD_DISABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==true) + if (m_dmr != NULL && m_dmrEnabled) processEnableCommand(m_dmrEnabled, false); break; case RCD_DISABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==true) + if (m_ysf != NULL && m_ysfEnabled) processEnableCommand(m_ysfEnabled, false); break; case RCD_DISABLE_P25: - if (m_p25 != NULL && m_p25Enabled==true) + if (m_p25 != NULL && m_p25Enabled) processEnableCommand(m_p25Enabled, false); break; case RCD_DISABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==true) + if (m_nxdn != NULL && m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, false); break; case RCD_DISABLE_FM: - if (m_fmEnabled == true) + if (m_fmEnabled) processEnableCommand(m_fmEnabled, false); break; case RCD_PAGE: diff --git a/MMDVMHost.h b/MMDVMHost.h index 17d6786..ea268c6 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -33,8 +33,10 @@ #include "YSFNetwork.h" #include "P25Network.h" #include "DMRNetwork.h" +#include "FMNetwork.h" #include "DMRLookup.h" #include "MobileGPS.h" +#include "FMControl.h" #include "Display.h" #include "Timer.h" #include "Modem.h" @@ -62,12 +64,14 @@ private: CP25Control* m_p25; CNXDNControl* m_nxdn; CPOCSAGControl* m_pocsag; + CFMControl* m_fm; CDStarNetwork* m_dstarNetwork; CDMRNetwork* m_dmrNetwork; CYSFNetwork* m_ysfNetwork; CP25Network* m_p25Network; CNXDNNetwork* m_nxdnNetwork; CPOCSAGNetwork* m_pocsagNetwork; + CFMNetwork* m_fmNetwork; CDisplay* m_display; CUMP* m_ump; unsigned char m_mode; @@ -82,6 +86,7 @@ private: unsigned int m_p25NetModeHang; unsigned int m_nxdnNetModeHang; unsigned int m_pocsagNetModeHang; + unsigned int m_fmNetModeHang; CTimer m_modeTimer; CTimer m_dmrTXTimer; CTimer m_cwIdTimer; @@ -114,6 +119,7 @@ private: bool createP25Network(); bool createNXDNNetwork(); bool createPOCSAGNetwork(); + bool createFMNetwork(); void remoteControl(); void processModeCommand(unsigned char mode, unsigned int timeout); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 812c7a4..6aee2cb 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -181,6 +181,8 @@ + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index fb8a362..55fda11 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -299,6 +299,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Modem.cpp b/Modem.cpp index edbebec..02276f2 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -79,6 +79,7 @@ const unsigned char MMDVM_POCSAG_DATA = 0x50U; const unsigned char MMDVM_FM_PARAMS1 = 0x60U; const unsigned char MMDVM_FM_PARAMS2 = 0x61U; const unsigned char MMDVM_FM_PARAMS3 = 0x62U; +const unsigned char MMDVM_FM_DATA = 0x65U; const unsigned char MMDVM_ACK = 0x70U; const unsigned char MMDVM_NAK = 0x7FU; @@ -150,6 +151,8 @@ 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_rxFMData(1000U, "Modem RX FM"), +m_txFMData(1000U, "Modem TX FM"), m_rxTransparentData(1000U, "Modem RX Transparent"), m_txTransparentData(1000U, "Modem TX Transparent"), m_sendTransparentDataFrameType(0U), @@ -163,6 +166,7 @@ m_ysfSpace(0U), m_p25Space(0U), m_nxdnSpace(0U), m_pocsagSpace(0U), +m_fmSpace(0U), m_tx(false), m_cd(false), m_lockout(false), @@ -572,6 +576,20 @@ void CModem::clock(unsigned int ms) } break; + case MMDVM_FM_DATA: { + if (m_trace) + CUtils::dump(1U, "RX FM Data", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxFMData.addData(&data, 1U); + + data = TAG_DATA; + m_rxFMData.addData(&data, 1U); + + m_rxFMData.addData(m_buffer + 3U, m_length - 3U); + } + break; + case MMDVM_GET_STATUS: { // if (m_trace) // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); @@ -615,9 +633,11 @@ void CModem::clock(unsigned int ms) m_nxdnSpace = m_buffer[11U]; if (m_length > 12U) m_pocsagSpace = m_buffer[12U]; + if (m_length > 13U) + m_fmSpace = m_buffer[13U]; m_inactivityTimer.start(); - // 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)); + // LogMessage("status=%02X, tx=%d, space=%u,%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, m_fmSpace, int(m_lockout), int(m_cd)); } break; @@ -821,6 +841,23 @@ void CModem::clock(unsigned int ms) m_pocsagSpace--; } + if (m_fmSpace > 1U && !m_txFMData.isEmpty()) { + unsigned char len = 0U; + m_txFMData.getData(&len, 1U); + m_txFMData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX FM Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing FM data to the MMDVM"); + + m_playoutTimer.start(); + + m_fmSpace--; + } + if (!m_txTransparentData.isEmpty()) { unsigned char len = 0U; m_txTransparentData.getData(&len, 1U); @@ -928,6 +965,20 @@ unsigned int CModem::readNXDNData(unsigned char* data) return len; } +unsigned int CModem::readFMData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxFMData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxFMData.getData(&len, 1U); + m_rxFMData.getData(data, len); + + return len; +} + unsigned int CModem::readTransparentData(unsigned char* data) { assert(data != NULL); @@ -1169,6 +1220,36 @@ bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) return true; } +unsigned int CModem::getFMSpace() const +{ + return (m_txFMData.freeSpace() * 2U) / 3U; +} + +bool CModem::writeFMData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + length = (length * 2U) / 3U; + + if (length > 252U) + return false; + + unsigned char buffer[255U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_FM_DATA; + + ::memcpy(buffer + 3U, data, length); + + unsigned char len = length + 3U; + m_txFMData.addData(&len, 1U); + m_txFMData.addData(buffer, len); + + return true; +} + bool CModem::writeTransparentData(const unsigned char* data, unsigned int length) { assert(data != NULL); diff --git a/Modem.h b/Modem.h index 6404d89..7605225 100644 --- a/Modem.h +++ b/Modem.h @@ -57,6 +57,7 @@ public: virtual unsigned int readYSFData(unsigned char* data); virtual unsigned int readP25Data(unsigned char* data); virtual unsigned int readNXDNData(unsigned char* data); + virtual unsigned int readFMData(unsigned char* data); virtual unsigned int readTransparentData(unsigned char* data); virtual unsigned int readSerial(unsigned char* data, unsigned int length); @@ -68,6 +69,7 @@ public: virtual bool hasP25Space() const; virtual bool hasNXDNSpace() const; virtual bool hasPOCSAGSpace() const; + virtual unsigned int getFMSpace() const; virtual bool hasTX() const; virtual bool hasCD() const; @@ -83,6 +85,7 @@ public: virtual bool writeP25Data(const unsigned char* data, unsigned int length); virtual bool writeNXDNData(const unsigned char* data, unsigned int length); virtual bool writePOCSAGData(const unsigned char* data, unsigned int length); + virtual bool writeFMData(const unsigned char* data, unsigned int length); virtual bool writeTransparentData(const unsigned char* data, unsigned int length); @@ -165,6 +168,8 @@ private: CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; CRingBuffer m_txPOCSAGData; + CRingBuffer m_rxFMData; + CRingBuffer m_txFMData; CRingBuffer m_rxTransparentData; CRingBuffer m_txTransparentData; unsigned int m_sendTransparentDataFrameType; @@ -178,6 +183,7 @@ private: unsigned int m_p25Space; unsigned int m_nxdnSpace; unsigned int m_pocsagSpace; + unsigned int m_fmSpace; bool m_tx; bool m_cd; bool m_lockout;