/* * Copyright (C) 2015-2021 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 "MMDVMHost.h" #include "DMRDirectNetwork.h" #include "DMRGatewayNetwork.h" #include "RSSIInterpolator.h" #include "NullController.h" #include "UARTController.h" #if defined(__linux__) #include "I2CController.h" #endif #include "UDPController.h" #include "Version.h" #include "StopWatch.h" #include "Defines.h" #include "Thread.h" #include "Log.h" #include "GitVersion.h" #include #include #include #if !defined(_WIN32) && !defined(_WIN64) #include #include #include #include #include #endif #if defined(_WIN32) || defined(_WIN64) const char* DEFAULT_INI_FILE = "MMDVM.ini"; #else const char* DEFAULT_INI_FILE = "/etc/MMDVM.ini"; #endif static bool m_killed = false; static int m_signal = 0; static bool m_reload = false; #if !defined(_WIN32) && !defined(_WIN64) static void sigHandler1(int signum) { m_killed = true; m_signal = signum; } static void sigHandler2(int signum) { m_reload = true; } #endif const char* HEADER1 = "Modified MMDVM Host by Carsten Schmiemann 2022"; const char* HEADER2 = "----------------------------------------------"; const char* HEADER3 = "DMR Mode only, board will be locked to DMR"; const char* HEADER4 = "Original project by gk4lx"; int main(int argc, char** argv) { const char* iniFile = DEFAULT_INI_FILE; if (argc > 1) { for (int currentArg = 1; currentArg < argc; ++currentArg) { std::string arg = argv[currentArg]; if ((arg == "-v") || (arg == "--version")) { ::fprintf(stdout, "MMDVMHost version %s git #%.7s\n", VERSION, gitversion); return 0; } else if (arg.substr(0,1) == "-") { ::fprintf(stderr, "Usage: MMDVMHost [-v|--version] [filename]\n"); return 1; } else { iniFile = argv[currentArg]; } } } #if !defined(_WIN32) && !defined(_WIN64) ::signal(SIGINT, sigHandler1); ::signal(SIGTERM, sigHandler1); ::signal(SIGHUP, sigHandler1); ::signal(SIGUSR1, sigHandler2); #endif int ret = 0; do { m_signal = 0; CMMDVMHost* host = new CMMDVMHost(std::string(iniFile)); ret = host->run(); delete host; if (m_signal == 2) ::LogInfo("MMDVMHost-%s exited on receipt of SIGINT", VERSION); if (m_signal == 15) ::LogInfo("MMDVMHost-%s exited on receipt of SIGTERM", VERSION); if (m_signal == 1) ::LogInfo("MMDVMHost-%s is restarting on receipt of SIGHUP", VERSION); } while (m_signal == 1); ::LogFinalise(); return ret; } CMMDVMHost::CMMDVMHost(const std::string& confFile) : m_conf(confFile), m_modem(NULL), m_dmr(NULL), m_dmrNetwork(NULL), m_display(NULL), m_mode(MODE_IDLE), m_dstarRFModeHang(10U), m_dmrRFModeHang(10U), m_ysfRFModeHang(10U), m_p25RFModeHang(10U), m_nxdnRFModeHang(10U), m_m17RFModeHang(10U), m_fmRFModeHang(10U), m_dstarNetModeHang(3U), m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), m_m17NetModeHang(3U), m_pocsagNetModeHang(3U), m_fmNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), m_duplex(false), m_timeout(180U), m_dstarEnabled(false), m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), m_m17Enabled(false), m_pocsagEnabled(false), m_fmEnabled(false), m_ax25Enabled(false), m_cwIdTime(0U), m_dmrLookup(NULL), m_callsign(), m_id(0U), m_cwCallsign(), m_lockFileEnabled(false), m_lockFileName(), m_remoteControl(NULL), m_fixedMode(true) { CUDPSocket::startup(); } CMMDVMHost::~CMMDVMHost() { CUDPSocket::shutdown(); } int CMMDVMHost::run() { bool ret = m_conf.read(); if (!ret) { ::fprintf(stderr, "MMDVMHost: cannot read the .ini file\n"); return 1; } #if !defined(_WIN32) && !defined(_WIN64) bool m_daemon = m_conf.getDaemon(); if (m_daemon) { // Create new process pid_t pid = ::fork(); if (pid == -1) { ::fprintf(stderr, "Couldn't fork() , exiting\n"); return -1; } else if (pid != 0) { exit(EXIT_SUCCESS); } // Create new session and process group if (::setsid() == -1){ ::fprintf(stderr, "Couldn't setsid(), exiting\n"); return -1; } // Set the working directory to the root directory if (::chdir("/") == -1){ ::fprintf(stderr, "Couldn't cd /, exiting\n"); return -1; } #if !defined(HD44780) && !defined(OLED) && !defined(_OPENWRT) // If we are currently root... if (getuid() == 0) { struct passwd* user = ::getpwnam("mmdvm"); if (user == NULL) { ::fprintf(stderr, "Could not get the mmdvm user, exiting\n"); return -1; } uid_t mmdvm_uid = user->pw_uid; gid_t mmdvm_gid = user->pw_gid; // Set user and group ID's to mmdvm:mmdvm if (::setgid(mmdvm_gid) != 0) { ::fprintf(stderr, "Could not set mmdvm GID, exiting\n"); return -1; } if (::setuid(mmdvm_uid) != 0) { ::fprintf(stderr, "Could not set mmdvm UID, exiting\n"); return -1; } // Double check it worked (AKA Paranoia) if (::setuid(0) != -1){ ::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n"); return -1; } } } #else ::fprintf(stderr, "Dropping root permissions in daemon mode is disabled.\n"); } #endif #endif #if !defined(_WIN32) && !defined(_WIN64) ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate()); #else ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate()); #endif if (!ret) { ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); return 1; } #if !defined(_WIN32) && !defined(_WIN64) if (m_daemon) { ::close(STDIN_FILENO); ::close(STDOUT_FILENO); } #endif LogInfo(HEADER1); LogInfo(HEADER2); LogInfo(HEADER3); LogInfo(HEADER4); LogMessage("MMDVMHost-%s is starting", VERSION); LogMessage("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion); readParams(); ret = createModem(); if (!ret) return 1; if (m_dmrEnabled && !m_modem->hasDMR()) { LogWarning("DMR enabled in the host but not in the modem firmware, disabling"); m_dmrEnabled = false; } m_display = CDisplay::createDisplay(m_conf, m_modem); if (m_dmrEnabled && m_conf.getDMRNetworkEnabled()) { ret = createDMRNetwork(); if (!ret) return 1; } sockaddr_storage transparentAddress; unsigned int transparentAddrLen; CUDPSocket* transparentSocket = NULL; unsigned int sendFrameType = 0U; if (m_conf.getTransparentEnabled()) { std::string remoteAddress = m_conf.getTransparentRemoteAddress(); unsigned short remotePort = m_conf.getTransparentRemotePort(); unsigned short localPort = m_conf.getTransparentLocalPort(); sendFrameType = m_conf.getTransparentSendFrameType(); LogInfo("Transparent Data"); LogInfo(" Remote Address: %s", remoteAddress.c_str()); LogInfo(" Remote Port: %hu", remotePort); LogInfo(" Local Port: %hu", localPort); LogInfo(" Send Frame Type: %u", sendFrameType); if (CUDPSocket::lookup(remoteAddress, remotePort, transparentAddress, transparentAddrLen) != 0) { LogError("Unable to resolve the address of the Transparent Data source"); return 1; } transparentSocket = new CUDPSocket(localPort); ret = transparentSocket->open(transparentAddress); if (!ret) { LogWarning("Could not open the Transparent data socket, disabling"); delete transparentSocket; transparentSocket = NULL; sendFrameType=0; } m_modem->setTransparentDataParams(sendFrameType); } if (m_conf.getLockFileEnabled()) { m_lockFileEnabled = true; m_lockFileName = m_conf.getLockFileName(); LogInfo("Lock File Parameters"); LogInfo(" Name: %s", m_lockFileName.c_str()); removeLockFile(); } if (m_conf.getCWIdEnabled()) { unsigned int time = m_conf.getCWIdTime(); m_cwCallsign = m_conf.getCWIdCallsign(); LogInfo("CW Id Parameters"); LogInfo(" Time: %u mins", time); LogInfo(" Callsign: %s", m_cwCallsign.c_str()); m_cwIdTime = time * 60U; m_cwIdTimer.setTimeout(m_cwIdTime / 4U); m_cwIdTimer.start(); } // For all modes we handle RSSI std::string rssiMappingFile = m_conf.getModemRSSIMappingFile(); CRSSIInterpolator* rssi = new CRSSIInterpolator; if (!rssiMappingFile.empty()) { LogInfo("RSSI"); LogInfo(" Mapping File: %s", rssiMappingFile.c_str()); rssi->load(rssiMappingFile); } // For DMR and P25 we try to map IDs to callsigns if (m_dmrEnabled) { std::string lookupFile = m_conf.getDMRIdLookupFile(); unsigned int reloadTime = m_conf.getDMRIdLookupTime(); LogInfo("DMR Id Lookups"); LogInfo(" File: %s", lookupFile.length() > 0U ? lookupFile.c_str() : "None"); if (reloadTime > 0U) LogInfo(" Reload: %u hours", reloadTime); m_dmrLookup = new CDMRLookup(lookupFile, reloadTime); m_dmrLookup->read(); } CStopWatch stopWatch; stopWatch.start(); DMR_BEACONS dmrBeacons = DMR_BEACONS_OFF; CTimer dmrBeaconIntervalTimer(1000U); CTimer dmrBeaconDurationTimer(1000U); if (m_dmrEnabled) { unsigned int id = m_conf.getDMRId(); unsigned int colorCode = m_conf.getDMRColorCode(); bool selfOnly = m_conf.getDMRSelfOnly(); bool embeddedLCOnly = m_conf.getDMREmbeddedLCOnly(); bool dumpTAData = m_conf.getDMRDumpTAData(); std::vector prefixes = m_conf.getDMRPrefixes(); std::vector blackList = m_conf.getDMRBlackList(); std::vector whiteList = m_conf.getDMRWhiteList(); std::vector slot1TGWhiteList = m_conf.getDMRSlot1TGWhiteList(); std::vector slot2TGWhiteList = m_conf.getDMRSlot2TGWhiteList(); unsigned int callHang = m_conf.getDMRCallHang(); unsigned int txHang = m_conf.getDMRTXHang(); unsigned int jitter = m_conf.getDMRNetworkJitter(); m_dmrRFModeHang = m_conf.getDMRModeHang(); dmrBeacons = m_conf.getDMRBeacons(); DMR_OVCM_TYPES ovcm = m_conf.getDMROVCM(); if (txHang > m_dmrRFModeHang) txHang = m_dmrRFModeHang; if (m_conf.getDMRNetworkEnabled()) { if (txHang > m_dmrNetModeHang) txHang = m_dmrNetModeHang; } if (callHang > txHang) callHang = txHang; LogInfo("DMR RF Parameters"); LogInfo(" Id: %u", id); LogInfo(" Color Code: %u", colorCode); LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); LogInfo(" Embedded LC Only: %s", embeddedLCOnly ? "yes" : "no"); LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no"); LogInfo(" Prefixes: %u", prefixes.size()); if (blackList.size() > 0U) LogInfo(" Source ID Black List: %u", blackList.size()); if (whiteList.size() > 0U) LogInfo(" Source ID White List: %u", whiteList.size()); if (slot1TGWhiteList.size() > 0U) LogInfo(" Slot 1 TG White List: %u", slot1TGWhiteList.size()); if (slot2TGWhiteList.size() > 0U) LogInfo(" Slot 2 TG White List: %u", slot2TGWhiteList.size()); LogInfo(" Call Hang: %us", callHang); LogInfo(" TX Hang: %us", txHang); LogInfo(" Mode Hang: %us", m_dmrRFModeHang); if (ovcm == DMR_OVCM_OFF) LogInfo(" OVCM: off"); else if (ovcm == DMR_OVCM_RX_ON) LogInfo(" OVCM: on(rx only)"); else if (ovcm == DMR_OVCM_TX_ON) LogInfo(" OVCM: on(tx only)"); else if (ovcm == DMR_OVCM_ON) LogInfo(" OVCM: on"); else if (ovcm == DMR_OVCM_FORCE_OFF) LogInfo(" OVCM: off (forced)"); switch (dmrBeacons) { case DMR_BEACONS_NETWORK: { unsigned int dmrBeaconDuration = m_conf.getDMRBeaconDuration(); LogInfo(" DMR Roaming Beacons Type: network"); LogInfo(" DMR Roaming Beacons Duration: %us", dmrBeaconDuration); dmrBeaconDurationTimer.setTimeout(dmrBeaconDuration); } break; case DMR_BEACONS_TIMED: { unsigned int dmrBeaconInterval = m_conf.getDMRBeaconInterval(); unsigned int dmrBeaconDuration = m_conf.getDMRBeaconDuration(); LogInfo(" DMR Roaming Beacons Type: timed"); LogInfo(" DMR Roaming Beacons Interval: %us", dmrBeaconInterval); LogInfo(" DMR Roaming Beacons Duration: %us", dmrBeaconDuration); dmrBeaconDurationTimer.setTimeout(dmrBeaconDuration); dmrBeaconIntervalTimer.setTimeout(dmrBeaconInterval); dmrBeaconIntervalTimer.start(); } break; default: LogInfo(" DMR Roaming Beacons Type: off"); break; } m_dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_dmrLookup, rssi, jitter, ovcm); m_dmrTXTimer.setTimeout(txHang); } bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { std::string address = m_conf.getRemoteControlAddress(); unsigned short port = m_conf.getRemoteControlPort(); LogInfo("Remote Control Parameters"); LogInfo(" Address: %s", address.c_str()); LogInfo(" Port: %hu", port); m_remoteControl = new CRemoteControl(this, address, port); ret = m_remoteControl->open(); if (!ret) { delete m_remoteControl; m_remoteControl = NULL; } } setMode(MODE_DMR); LogMessage("MMDVMHost-%s is running", VERSION); while (!m_killed) { bool lockout = m_modem->hasLockout(); if (lockout && m_mode != MODE_LOCKOUT) setMode(MODE_LOCKOUT); else if (!lockout && m_mode == MODE_LOCKOUT) setMode(MODE_IDLE); bool error = m_modem->hasError(); if (error && m_mode != MODE_ERROR) setMode(MODE_ERROR); else if (!error && m_mode == MODE_ERROR) setMode(MODE_IDLE); unsigned char data[500U]; unsigned int len; bool ret; len = m_modem->readDMRData1(data); if (m_dmr != NULL && len > 0U) { if (m_mode == MODE_IDLE) { if (m_duplex) { bool ret = m_dmr->processWakeup(data); if (ret) { m_modeTimer.setTimeout(m_dmrRFModeHang); setMode(MODE_DMR); dmrBeaconDurationTimer.stop(); } } else { m_modeTimer.setTimeout(m_dmrRFModeHang); setMode(MODE_DMR); m_dmr->writeModemSlot1(data, len); dmrBeaconDurationTimer.stop(); } } else if (m_mode == MODE_DMR) { if (m_duplex && !m_modem->hasTX()) { bool ret = m_dmr->processWakeup(data); if (ret) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } } else { bool ret = m_dmr->writeModemSlot1(data, len); if (ret) { dmrBeaconDurationTimer.stop(); m_modeTimer.start(); if (m_duplex) m_dmrTXTimer.start(); } } } else if (m_mode != MODE_LOCKOUT) { LogWarning("DMR modem data received when in mode %u", m_mode); } } len = m_modem->readDMRData2(data); if (m_dmr != NULL && len > 0U) { if (m_mode == MODE_IDLE) { if (m_duplex) { bool ret = m_dmr->processWakeup(data); if (ret) { m_modeTimer.setTimeout(m_dmrRFModeHang); setMode(MODE_DMR); dmrBeaconDurationTimer.stop(); } } else { m_modeTimer.setTimeout(m_dmrRFModeHang); setMode(MODE_DMR); m_dmr->writeModemSlot2(data, len); dmrBeaconDurationTimer.stop(); } } else if (m_mode == MODE_DMR) { if (m_duplex && !m_modem->hasTX()) { bool ret = m_dmr->processWakeup(data); if (ret) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } } else { bool ret = m_dmr->writeModemSlot2(data, len); if (ret) { dmrBeaconDurationTimer.stop(); m_modeTimer.start(); if (m_duplex) m_dmrTXTimer.start(); } } } else if (m_mode != MODE_LOCKOUT) { LogWarning("DMR modem data received when in mode %u", m_mode); } } len = m_modem->readTransparentData(data); if (transparentSocket != NULL && len > 0U) transparentSocket->write(data, len, transparentAddress, transparentAddrLen); if (!m_fixedMode) { if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) setMode(MODE_IDLE); } if (m_dmr != NULL) { ret = m_modem->hasDMRSpace1(); if (ret) { len = m_dmr->readModemSlot1(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_dmrNetModeHang); setMode(MODE_DMR); } if (m_mode == MODE_DMR) { if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_modem->writeDMRData1(data, len); dmrBeaconDurationTimer.stop(); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("DMR data received when in mode %u", m_mode); } } } ret = m_modem->hasDMRSpace2(); if (ret) { len = m_dmr->readModemSlot2(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_dmrNetModeHang); setMode(MODE_DMR); } if (m_mode == MODE_DMR) { if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_modem->writeDMRData2(data, len); dmrBeaconDurationTimer.stop(); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("DMR data received when in mode %u", m_mode); } } } } if (transparentSocket != NULL) { sockaddr_storage address; unsigned int addrlen; len = transparentSocket->read(data, 200U, address, addrlen); if (len > 0U) m_modem->writeTransparentData(data, len); } remoteControl(); unsigned int ms = stopWatch.elapsed(); stopWatch.start(); m_display->clock(ms); m_modem->clock(ms); if (!m_fixedMode) m_modeTimer.clock(ms); if (m_reload) { if (m_dmrLookup != NULL) m_dmrLookup->reload(); m_reload = false; } if (m_dmr != NULL) m_dmr->clock(); if (m_dmrNetwork != NULL) m_dmrNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { if (!m_modem->hasTX()){ LogDebug("sending CW ID"); m_display->writeCW(); m_modem->sendCWId(m_cwCallsign); m_cwIdTimer.setTimeout(m_cwIdTime); m_cwIdTimer.start(); } } switch (dmrBeacons) { case DMR_BEACONS_TIMED: dmrBeaconIntervalTimer.clock(ms); if (dmrBeaconIntervalTimer.isRunning() && dmrBeaconIntervalTimer.hasExpired()) { if ((m_mode == MODE_IDLE || m_mode == MODE_DMR) && !m_modem->hasTX()) { if (!m_fixedMode && m_mode == MODE_IDLE) setMode(MODE_DMR); dmrBeaconIntervalTimer.start(); dmrBeaconDurationTimer.start(); } } break; case DMR_BEACONS_NETWORK: if (m_dmrNetwork != NULL) { bool beacon = m_dmrNetwork->wantsBeacon(); if (beacon) { if ((m_mode == MODE_IDLE || m_mode == MODE_DMR) && !m_modem->hasTX()) { if (!m_fixedMode && m_mode == MODE_IDLE) setMode(MODE_DMR); dmrBeaconDurationTimer.start(); } } } break; default: break; } dmrBeaconDurationTimer.clock(ms); if (dmrBeaconDurationTimer.isRunning() && dmrBeaconDurationTimer.hasExpired()) { if (!m_fixedMode) setMode(MODE_IDLE); dmrBeaconDurationTimer.stop(); } m_dmrTXTimer.clock(ms); if (m_dmrTXTimer.isRunning() && m_dmrTXTimer.hasExpired()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } if (ms < 5U) CThread::sleep(5U); } setMode(MODE_QUIT); m_modem->close(); delete m_modem; m_display->close(); delete m_display; if (m_dmrLookup != NULL) m_dmrLookup->stop(); if (m_dmrNetwork != NULL) { m_dmrNetwork->close(true); delete m_dmrNetwork; } if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; } if (m_remoteControl != NULL) { m_remoteControl->close(); delete m_remoteControl; } delete m_dmr; return 0; } bool CMMDVMHost::createModem() { std::string protocol = m_conf.getModemProtocol(); std::string uartPort = m_conf.getModemUARTPort(); unsigned int uartSpeed = m_conf.getModemUARTSpeed(); std::string i2cPort = m_conf.getModemI2CPort(); unsigned int i2cAddress = m_conf.getModemI2CAddress(); std::string modemAddress = m_conf.getModemModemAddress(); unsigned short modemPort = m_conf.getModemModemPort(); std::string localAddress = m_conf.getModemLocalAddress(); unsigned short localPort = m_conf.getModemLocalPort(); bool rxInvert = m_conf.getModemRXInvert(); bool txInvert = m_conf.getModemTXInvert(); bool pttInvert = m_conf.getModemPTTInvert(); unsigned int txDelay = m_conf.getModemTXDelay(); unsigned int dmrDelay = m_conf.getModemDMRDelay(); float rxLevel = m_conf.getModemRXLevel(); float cwIdTXLevel = m_conf.getModemCWIdTXLevel(); float dmrTXLevel = m_conf.getModemDMRTXLevel(); bool trace = m_conf.getModemTrace(); bool debug = m_conf.getModemDebug(); unsigned int colorCode = m_conf.getDMRColorCode(); unsigned int rxFrequency = m_conf.getRXFrequency(); unsigned int txFrequency = m_conf.getTXFrequency(); int rxOffset = m_conf.getModemRXOffset(); int txOffset = m_conf.getModemTXOffset(); int rxDCOffset = m_conf.getModemRXDCOffset(); int txDCOffset = m_conf.getModemTXDCOffset(); float rfLevel = m_conf.getModemRFLevel(); bool useCOSAsLockout = m_conf.getModemUseCOSAsLockout(); LogInfo("Modem Parameters"); LogInfo(" Protocol: %s", protocol.c_str()); if (protocol == "uart") { LogInfo(" UART Port: %s", uartPort.c_str()); LogInfo(" UART Speed: %u", uartSpeed); } else if (protocol == "udp") { LogInfo(" Modem Address: %s", modemAddress.c_str()); LogInfo(" Modem Port: %hu", modemPort); LogInfo(" Local Address: %s", localAddress.c_str()); LogInfo(" Local Port: %hu", localPort); } #if defined(__linux__) else if (protocol == "i2c") { LogInfo(" I2C Port: %s", i2cPort.c_str()); LogInfo(" I2C Address: %02X", i2cAddress); } #endif LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); LogInfo(" TX Delay: %ums", txDelay); LogInfo(" RX Offset: %dHz", rxOffset); LogInfo(" TX Offset: %dHz", txOffset); LogInfo(" RX DC Offset: %d", rxDCOffset); LogInfo(" TX DC Offset: %d", txDCOffset); LogInfo(" RF Level: %.1f%%", rfLevel); LogInfo(" DMR Delay: %u (%.1fms)", dmrDelay, float(dmrDelay) * 0.0416666F); LogInfo(" RX Level: %.1f%%", rxLevel); LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); LogInfo(" Use COS as Lockout: %s", useCOSAsLockout ? "yes" : "no"); m_modem = new CModem(m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug); IModemPort* port = NULL; if (protocol == "uart") port = new CUARTController(uartPort, uartSpeed, true); else if (protocol == "udp") port = new CUDPController(modemAddress, modemPort, localAddress, localPort); #if defined(__linux__) else if (protocol == "i2c") port = new CI2CController(i2cPort, i2cAddress); #endif else if (protocol == "null") port = new CNullController; else return false; m_modem->setPort(port); m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_m17Enabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel); m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel); m_modem->setDMRParams(colorCode); bool ret = m_modem->open(); if (!ret) { delete m_modem; m_modem = NULL; return false; } return true; } bool CMMDVMHost::createDMRNetwork() { std::string remoteAddress = m_conf.getDMRNetworkRemoteAddress(); unsigned short remotePort = m_conf.getDMRNetworkRemotePort(); std::string localAddress = m_conf.getDMRNetworkLocalAddress(); unsigned short localPort = m_conf.getDMRNetworkLocalPort(); unsigned int id = m_conf.getDMRId(); std::string password = m_conf.getDMRNetworkPassword(); bool debug = m_conf.getDMRNetworkDebug(); unsigned int jitter = m_conf.getDMRNetworkJitter(); bool slot1 = m_conf.getDMRNetworkSlot1(); bool slot2 = m_conf.getDMRNetworkSlot2(); HW_TYPE hwType = m_modem->getHWType(); m_dmrNetModeHang = m_conf.getDMRNetworkModeHang(); std::string options = m_conf.getDMRNetworkOptions(); std::string type = m_conf.getDMRNetworkType(); LogInfo("DMR Network Parameters"); LogInfo(" Type: %s", type.c_str()); LogInfo(" Remote Address: %s", remoteAddress.c_str()); LogInfo(" Remote Port: %hu", remotePort); LogInfo(" Local Address: %s", localAddress.c_str()); LogInfo(" Local Port: %hu", localPort); LogInfo(" Jitter: %ums", jitter); LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Mode Hang: %us", m_dmrNetModeHang); if (type == "Direct") m_dmrNetwork = new CDMRDirectNetwork(remoteAddress, remotePort, localAddress, localPort, id, password, m_duplex, VERSION, slot1, slot2, hwType, debug); else m_dmrNetwork = new CDMRGatewayNetwork(remoteAddress, remotePort, localAddress, localPort, id, m_duplex, VERSION, slot1, slot2, hwType, debug); unsigned int rxFrequency = m_conf.getRXFrequency(); unsigned int txFrequency = m_conf.getTXFrequency(); unsigned int power = m_conf.getPower(); unsigned int colorCode = m_conf.getDMRColorCode(); LogInfo("Info Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); LogInfo(" RX Frequency: %uHz", rxFrequency); LogInfo(" TX Frequency: %uHz", txFrequency); LogInfo(" Power: %uW", power); if (type == "Direct") { float latitude = m_conf.getLatitude(); float longitude = m_conf.getLongitude(); int height = m_conf.getHeight(); std::string location = m_conf.getLocation(); std::string description = m_conf.getDescription(); std::string url = m_conf.getURL(); LogInfo(" Latitude: %fdeg N", latitude); LogInfo(" Longitude: %fdeg E", longitude); LogInfo(" Height: %um", height); LogInfo(" Location: \"%s\"", location.c_str()); LogInfo(" Description: \"%s\"", description.c_str()); LogInfo(" URL: \"%s\"", url.c_str()); m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode, latitude, longitude, height, location, description, url); } else { m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode, 0.0F, 0.0F, 0, "", "", ""); } if (!options.empty()) { LogInfo(" Options: %s", options.c_str()); m_dmrNetwork->setOptions(options); } bool ret = m_dmrNetwork->open(); if (!ret) { delete m_dmrNetwork; m_dmrNetwork = NULL; return false; } m_dmrNetwork->enable(true); return true; } void CMMDVMHost::readParams() { m_dmrEnabled = m_conf.getDMREnabled(); 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()); LogInfo(" Id: %u", m_id); LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); LogInfo(" Timeout: %us", m_timeout); LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); } void CMMDVMHost::setMode(unsigned char mode) { assert(m_modem != NULL); assert(m_display != NULL); switch(mode) { case MODE_DMR: if (m_dmrNetwork != NULL) m_dmrNetwork->enable(true); m_modem->setMode(MODE_DMR); if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_mode = MODE_DMR; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("DMR"); break; case MODE_LOCKOUT: if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); if (m_dmr != NULL) m_dmr->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } m_modem->setMode(MODE_IDLE); m_display->setLockout(); m_mode = MODE_LOCKOUT; m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); break; case MODE_ERROR: LogMessage("Mode set to Error"); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); if (m_dmr != NULL) m_dmr->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } m_display->setError("MODEM"); m_mode = MODE_ERROR; m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); break; default: if (m_dmrNetwork != NULL) m_dmrNetwork->enable(true); if (m_dmr != NULL) m_dmr->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } m_modem->setMode(MODE_IDLE); if (m_mode == MODE_ERROR) { m_modem->sendCWId(m_callsign); m_cwIdTimer.setTimeout(m_cwIdTime); m_cwIdTimer.start(); } else { m_cwIdTimer.setTimeout(m_cwIdTime / 4U); m_cwIdTimer.start(); } m_display->setIdle(); if (mode == MODE_QUIT) m_display->setQuit(); m_mode = MODE_IDLE; m_modeTimer.stop(); removeLockFile(); break; } } void CMMDVMHost::createLockFile(const char* mode) const { if (m_lockFileEnabled) { FILE* fp = ::fopen(m_lockFileName.c_str(), "wt"); if (fp != NULL) { ::fprintf(fp, "%s\n", mode); ::fclose(fp); } } } void CMMDVMHost::removeLockFile() const { if (m_lockFileEnabled) ::remove(m_lockFileName.c_str()); } void CMMDVMHost::remoteControl() { if (m_remoteControl == NULL) return; REMOTE_COMMAND command = m_remoteControl->getCommand(); switch (command) { case RCD_MODE_IDLE: m_fixedMode = false; setMode(MODE_IDLE); break; case RCD_MODE_LOCKOUT: m_fixedMode = false; setMode(MODE_LOCKOUT); break; case RCD_MODE_DMR: if (m_dmr != NULL) processModeCommand(MODE_DMR, m_dmrRFModeHang); break; case RCD_ENABLE_DMR: if (m_dmr != NULL && !m_dmrEnabled) processEnableCommand(m_dmrEnabled, true); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(true); break; case RCD_DISABLE_DMR: if (m_dmr != NULL && m_dmrEnabled) processEnableCommand(m_dmrEnabled, false); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); break; case RCD_CW: setMode(MODE_IDLE); // Force the modem to go idle so that we can send the CW text. if (!m_modem->hasTX()) { std::string cwtext; for (unsigned int i = 0U; i < m_remoteControl->getArgCount(); i++) { if (i > 0U) cwtext += " "; cwtext += m_remoteControl->getArgString(i); } m_display->writeCW(); m_modem->sendCWId(cwtext); } break; case RCD_RELOAD: m_reload = true; break; default: break; } } void CMMDVMHost::processModeCommand(unsigned char mode, unsigned int timeout) { m_fixedMode = false; m_modeTimer.setTimeout(timeout); if (m_remoteControl->getArgCount() > 0U) { if (m_remoteControl->getArgString(0U) == "fixed") { m_fixedMode = true; } else { unsigned int t = m_remoteControl->getArgUInt(0U); if (t > 0U) m_modeTimer.setTimeout(t); } } setMode(mode); } void CMMDVMHost::processEnableCommand(bool& mode, bool enabled) { LogDebug("Setting mode current=%s new=%s",mode ? "true" : "false",enabled ? "true" : "false"); mode = enabled; m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_m17Enabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); if (!m_modem->writeConfig()) LogError("Cannot write Config to MMDVM"); } void CMMDVMHost::buildNetworkStatusString(std::string &str) { str = ""; str += std::string(" dmr:") + (((m_dmrNetwork == NULL) || (m_dmrEnabled == false)) ? "n/a" : (m_dmrNetwork->isConnected() ? "conn" : "disc")); str += std::string(" fm:") + (m_fmEnabled ? "conn" : "n/a"); } void CMMDVMHost::buildNetworkHostsString(std::string &str) { str = ""; str += std::string(" dmr:\"") + ((m_dmrEnabled && (m_dmrNetwork != NULL)) ? m_conf.getDMRNetworkRemoteAddress() : "NONE") + "\""; }