/* * Copyright (C) 2015,2016 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 "Log.h" #include "Version.h" #include "StopWatch.h" #include "Defines.h" #include "DStarControl.h" #include "DMRControl.h" #include "TFTSerial.h" #include "NullDisplay.h" #include "YSFControl.h" #include "Nextion.h" #include "Thread.h" #if defined(HD44780) #include "HD44780.h" #endif #if defined(OLED) #include "OLED.h" #endif #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; #if !defined(_WIN32) && !defined(_WIN64) static void sigHandler(int signum) { m_killed = true; m_signal = signum; } #endif const char* HEADER1 = "This software is for use on amateur radio networks only,"; const char* HEADER2 = "it is to be used for educational purposes only. Its use on"; const char* HEADER3 = "commercial networks is strictly prohibited."; const char* HEADER4 = "Copyright(C) 2015, 2016 by Jonathan Naylor, G4KLX and others"; 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\n", VERSION); 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(SIGTERM, sigHandler); ::signal(SIGHUP, sigHandler); #endif int ret = 0; do { m_signal = 0; CMMDVMHost* host = new CMMDVMHost(std::string(iniFile)); ret = host->run(); delete host; if (m_signal == 15) ::LogInfo("Caught SIGTERM, exiting"); if (m_signal == 1) ::LogInfo("Caught SIGHUP, restarting"); } while (m_signal == 1); ::LogFinalise(); return ret; } CMMDVMHost::CMMDVMHost(const std::string& confFile) : m_conf(confFile), m_modem(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_display(NULL), m_mode(MODE_IDLE), m_rfModeHang(10U), m_netModeHang(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_callsign() { } CMMDVMHost::~CMMDVMHost() { } int CMMDVMHost::run() { bool ret = m_conf.read(); if (!ret) { ::fprintf(stderr, "MMDVMHost: cannot read the .ini file\n"); return 1; } ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); if (!ret) { ::fprintf(stderr, "MMDVMHost: unable to open the log 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) { ::LogWarning("Couldn't fork() , exiting"); return -1; } else if (pid != 0) exit(EXIT_SUCCESS); // Create new session and process group if (::setsid() == -1){ ::LogWarning("Couldn't setsid(), exiting"); return -1; } // Set the working directory to the root directory if (::chdir("/") == -1){ ::LogWarning("Couldn't cd /, exiting"); return -1; } ::close(STDIN_FILENO); ::close(STDOUT_FILENO); ::close(STDERR_FILENO); #if !defined(HD44780) //If we are currently root... if (getuid() == 0) { struct passwd* user = ::getpwnam("mmdvm"); if (user == NULL) { ::LogError("Could not get the mmdvm user, exiting"); 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) { ::LogWarning("Could not set mmdvm GID, exiting"); return -1; } if (setuid(mmdvm_uid) != 0) { ::LogWarning("Could not set mmdvm UID, exiting"); return -1; } //Double check it worked (AKA Paranoia) if (setuid(0) != -1){ ::LogWarning("It's possible to regain root - something is wrong!, exiting"); return -1; } } } #else ::LogWarning("Dropping root permissions in daemon mode is disabled with HD44780 display"); } #endif #endif LogInfo(HEADER1); LogInfo(HEADER2); LogInfo(HEADER3); LogInfo(HEADER4); LogMessage("MMDVMHost-%s is starting", VERSION); readParams(); ret = createModem(); if (!ret) return 1; createDisplay(); if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { ret = createDStarNetwork(); if (!ret) return 1; } if (m_dmrEnabled && m_conf.getDMRNetworkEnabled()) { ret = createDMRNetwork(); if (!ret) return 1; } if (m_ysfEnabled && m_conf.getFusionNetworkEnabled()) { ret = createYSFNetwork(); if (!ret) return 1; } if (m_conf.getCWIdEnabled()) { unsigned int time = m_conf.getCWIdTime(); LogInfo("CW Id Parameters"); LogInfo(" Time: %u mins", time); m_cwIdTimer.setTimeout(time * 60U); m_cwIdTimer.start(); } CTimer dmrBeaconTimer(1000U, 4U); bool dmrBeaconsEnabled = m_dmrEnabled && m_conf.getDMRBeacons(); CStopWatch stopWatch; stopWatch.start(); CDStarControl* dstar = NULL; if (m_dstarEnabled) { std::string module = m_conf.getDStarModule(); bool selfOnly = m_conf.getDStarSelfOnly(); std::vector blackList = m_conf.getDStarBlackList(); LogInfo("D-Star Parameters"); LogInfo(" Module: %s", module.c_str()); LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); if (blackList.size() > 0U) LogInfo(" Black List: %u", blackList.size()); dstar = new CDStarControl(m_callsign, module, selfOnly, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex); } CDMRControl* dmr = NULL; if (m_dmrEnabled) { unsigned int id = m_conf.getDMRId(); unsigned int colorCode = m_conf.getDMRColorCode(); bool selfOnly = m_conf.getDMRSelfOnly(); std::vector prefixes = m_conf.getDMRPrefixes(); std::vector blackList = m_conf.getDMRBlackList(); std::vector dstIDBlackListSlot1RF = m_conf.getDMRDstIdBlacklistSlot1RF(); std::vector dstIDBlackListSlot2RF = m_conf.getDMRDstIdBlacklistSlot2RF(); std::vector dstIDWhiteListSlot1RF = m_conf.getDMRDstIdWhitelistSlot1RF(); std::vector dstIDWhiteListSlot2RF = m_conf.getDMRDstIdWhitelistSlot2RF(); std::vector dstIDBlackListSlot1NET = m_conf.getDMRDstIdBlacklistSlot1NET(); std::vector dstIDBlackListSlot2NET = m_conf.getDMRDstIdBlacklistSlot2NET(); std::vector dstIDWhiteListSlot1NET = m_conf.getDMRDstIdWhitelistSlot1NET(); std::vector dstIDWhiteListSlot2NET = m_conf.getDMRDstIdWhitelistSlot2NET(); std::string lookupFile = m_conf.getDMRLookupFile(); unsigned int callHang = m_conf.getDMRCallHang(); unsigned int txHang = m_conf.getDMRTXHang(); int rssiMultiplier = m_conf.getModemRSSIMultiplier(); int rssiOffset = m_conf.getModemRSSIOffset(); if (txHang > m_rfModeHang) txHang = m_rfModeHang; if (txHang > m_netModeHang) txHang = m_netModeHang; if (callHang > txHang) callHang = txHang; LogInfo("DMR Parameters"); LogInfo(" Id: %u", id); LogInfo(" Color Code: %u", colorCode); LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); LogInfo(" Prefixes: %u", prefixes.size()); if (blackList.size() > 0U) LogInfo(" Source ID Black List: %u", blackList.size()); if (dstIDBlackListSlot1RF.size() > 0U) LogInfo(" Slot 1 RF Destination ID Black List: %u entries", dstIDBlackListSlot1RF.size()); if (dstIDBlackListSlot2RF.size() > 0U) LogInfo(" Slot 2 RF Destination ID Black List: %u entries", dstIDBlackListSlot2RF.size()); if (dstIDWhiteListSlot1RF.size() > 0U) LogInfo(" Slot 1 RF Destination ID White List: %u entries", dstIDWhiteListSlot1RF.size()); if (dstIDWhiteListSlot2RF.size() > 0U) LogInfo(" Slot 2 RF Destination ID White List: %u entries", dstIDWhiteListSlot2RF.size()); if (dstIDBlackListSlot1NET.size() > 0U) LogInfo(" Slot 1 NET Destination ID Black List: %u entries", dstIDBlackListSlot1NET.size()); if (dstIDBlackListSlot2NET.size() > 0U) LogInfo(" Slot 2 NET Destination ID Black List: %u entries", dstIDBlackListSlot2NET.size()); if (dstIDWhiteListSlot1NET.size() > 0U) LogInfo(" Slot 1 NET Destination ID White List: %u entries", dstIDWhiteListSlot1NET.size()); if (dstIDWhiteListSlot2NET.size() > 0U) LogInfo(" Slot 2 NET Destination ID White List: %u entries", dstIDWhiteListSlot2NET.size()); LogInfo(" Lookup File: %s", lookupFile.length() > 0U ? lookupFile.c_str() : "None"); LogInfo(" Call Hang: %us", callHang); LogInfo(" TX Hang: %us", txHang); if (rssiMultiplier != 0) { LogInfo(" RSSI Multiplier: %d", rssiMultiplier); LogInfo(" RSSI Offset: %d", rssiOffset); } dmr = new CDMRControl(id, colorCode, callHang, selfOnly, prefixes, blackList,dstIDBlackListSlot1RF,dstIDWhiteListSlot1RF, dstIDBlackListSlot2RF, dstIDWhiteListSlot2RF, dstIDBlackListSlot1NET,dstIDWhiteListSlot1NET, dstIDBlackListSlot2NET, dstIDWhiteListSlot2NET, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, lookupFile, rssiMultiplier, rssiOffset); m_dmrTXTimer.setTimeout(txHang); } CYSFControl* ysf = NULL; if (m_ysfEnabled) ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, m_timeout, m_duplex); setMode(MODE_IDLE); 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[200U]; unsigned int len; bool ret; len = m_modem->readDStarData(data); if (dstar != NULL && len > 0U) { if (m_mode == MODE_IDLE) { bool ret = dstar->writeModem(data, len); if (ret) { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_DSTAR); } } else if (m_mode == MODE_DSTAR) { dstar->writeModem(data, len); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("D-Star modem data received when in mode %u", m_mode); } } len = m_modem->readDMRData1(data); if (dmr != NULL && len > 0U) { if (m_mode == MODE_IDLE) { if (m_duplex) { bool ret = dmr->processWakeup(data); if (ret) { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_DMR); dmrBeaconTimer.stop(); } } else { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_DMR); dmr->writeModemSlot1(data, len); dmrBeaconTimer.stop(); } } else if (m_mode == MODE_DMR) { if (m_duplex && !m_modem->hasTX()) { bool ret = dmr->processWakeup(data); if (ret) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } } else { dmr->writeModemSlot1(data, len); dmrBeaconTimer.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 (dmr != NULL && len > 0U) { if (m_mode == MODE_IDLE) { if (m_duplex) { bool ret = dmr->processWakeup(data); if (ret) { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_DMR); dmrBeaconTimer.stop(); } } else { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_DMR); dmr->writeModemSlot2(data, len); dmrBeaconTimer.stop(); } } else if (m_mode == MODE_DMR) { if (m_duplex && !m_modem->hasTX()) { bool ret = dmr->processWakeup(data); if (ret) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } } else { dmr->writeModemSlot2(data, len); dmrBeaconTimer.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->readYSFData(data); if (ysf != NULL && len > 0U) { if (m_mode == MODE_IDLE) { bool ret = ysf->writeModem(data, len); if (ret) { m_modeTimer.setTimeout(m_rfModeHang); setMode(MODE_YSF); } } else if (m_mode == MODE_YSF) { ysf->writeModem(data, len); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("System Fusion modem data received when in mode %u", m_mode); } } if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) setMode(MODE_IDLE); if (dstar != NULL) { ret = m_modem->hasDStarSpace(); if (ret) { len = dstar->readModem(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_netModeHang); setMode(MODE_DSTAR); } if (m_mode == MODE_DSTAR) { m_modem->writeDStarData(data, len); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("D-Star data received when in mode %u", m_mode); } } } } if (dmr != NULL) { ret = m_modem->hasDMRSpace1(); if (ret) { len = dmr->readModemSlot1(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_netModeHang); setMode(MODE_DMR); } if (m_mode == MODE_DMR) { if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_modem->writeDMRData1(data, len); dmrBeaconTimer.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 = dmr->readModemSlot2(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_netModeHang); setMode(MODE_DMR); } if (m_mode == MODE_DMR) { if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_modem->writeDMRData2(data, len); dmrBeaconTimer.stop(); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("DMR data received when in mode %u", m_mode); } } } } if (ysf != NULL) { ret = m_modem->hasYSFSpace(); if (ret) { len = ysf->readModem(data); if (len > 0U) { if (m_mode == MODE_IDLE) { m_modeTimer.setTimeout(m_netModeHang); setMode(MODE_YSF); } if (m_mode == MODE_YSF) { m_modem->writeYSFData(data, len); m_modeTimer.start(); } else if (m_mode != MODE_LOCKOUT) { LogWarning("System Fusion data received when in mode %u", m_mode); } } } } if (m_dmrNetwork != NULL) { bool run = m_dmrNetwork->wantsBeacon(); if (dmrBeaconsEnabled && run && m_mode == MODE_IDLE && !m_modem->hasTX()) { setMode(MODE_DMR); dmrBeaconTimer.start(); } } unsigned int ms = stopWatch.elapsed(); stopWatch.start(); m_display->clock(ms); m_modem->clock(ms); m_modeTimer.clock(ms); if (dstar != NULL) dstar->clock(); if (dmr != NULL) dmr->clock(); if (ysf != NULL) ysf->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); if (m_dmrNetwork != NULL) m_dmrNetwork->clock(ms); if (m_ysfNetwork != NULL) m_ysfNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { if (m_mode == MODE_IDLE && !m_modem->hasTX()){ LogDebug("sending CW ID"); m_modem->sendCWId(m_callsign); m_cwIdTimer.start(); //reset only after sending ID, timer-overflow after 49 days doesnt matter } } dmrBeaconTimer.clock(ms); if (dmrBeaconTimer.isRunning() && dmrBeaconTimer.hasExpired()) { setMode(MODE_IDLE); dmrBeaconTimer.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); } LogMessage("MMDVMHost-%s is exiting on receipt of SIGHUP1", VERSION); setMode(MODE_IDLE); m_modem->close(); delete m_modem; m_display->close(); delete m_display; if (m_dstarNetwork != NULL) { m_dstarNetwork->close(); delete m_dstarNetwork; } if (m_dmrNetwork != NULL) { m_dmrNetwork->close(); delete m_dmrNetwork; } if (m_ysfNetwork != NULL) { m_ysfNetwork->close(); delete m_ysfNetwork; } delete dstar; delete dmr; delete ysf; return 0; } bool CMMDVMHost::createModem() { std::string port = m_conf.getModemPort(); 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(); unsigned int rxLevel = m_conf.getModemRXLevel(); unsigned int txLevel = m_conf.getModemTXLevel(); bool debug = m_conf.getModemDebug(); unsigned int colorCode = m_conf.getDMRColorCode(); unsigned int rxFrequency = m_conf.getRxFrequency(); unsigned int txFrequency = m_conf.getTxFrequency(); int oscOffset = m_conf.getModemOscOffset(); LogInfo("Modem Parameters"); LogInfo(" Port: %s", port.c_str()); 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(" DMR Delay: %u (%.1fms)", dmrDelay, float(dmrDelay) * 0.0416666F); LogInfo(" RX Level: %u%%", rxLevel); LogInfo(" TX Level: %u%%", txLevel); LogInfo(" RX Frequency: %uHz", rxFrequency); LogInfo(" TX Frequency: %uHz", txFrequency); LogInfo(" Osc. Offset: %dppm", oscOffset); m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel, dmrDelay, oscOffset, debug); m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled); m_modem->setRFParams(rxFrequency, txFrequency); m_modem->setDMRParams(colorCode); bool ret = m_modem->open(); if (!ret) { delete m_modem; m_modem = NULL; return false; } return true; } bool CMMDVMHost::createDStarNetwork() { std::string gatewayAddress = m_conf.getDStarGatewayAddress(); unsigned int gatewayPort = m_conf.getDStarGatewayPort(); unsigned int localPort = m_conf.getDStarLocalPort(); bool debug = m_conf.getDStarNetworkDebug(); LogInfo("D-Star Network Parameters"); LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); LogInfo(" Gateway Port: %u", gatewayPort); LogInfo(" Local Port: %u", localPort); m_dstarNetwork = new CDStarNetwork(gatewayAddress, gatewayPort, localPort, m_duplex, VERSION, debug); bool ret = m_dstarNetwork->open(); if (!ret) { delete m_dstarNetwork; m_dstarNetwork = NULL; return false; } m_dstarNetwork->enable(true); return true; } bool CMMDVMHost::createDMRNetwork() { std::string address = m_conf.getDMRNetworkAddress(); unsigned int port = m_conf.getDMRNetworkPort(); unsigned int local = m_conf.getDMRNetworkLocal(); unsigned int id = m_conf.getDMRId(); std::string password = m_conf.getDMRNetworkPassword(); bool debug = m_conf.getDMRNetworkDebug(); bool slot1 = m_conf.getDMRNetworkSlot1(); bool slot2 = m_conf.getDMRNetworkSlot2(); bool rssi = m_conf.getDMRNetworkRSSI(); LogInfo("DMR Network Parameters"); LogInfo(" Address: %s", address.c_str()); LogInfo(" Port: %u", port); if (local > 0U) LogInfo(" Local: %u", local); else LogInfo(" Local: random"); LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" RSSI: %s", rssi ? "enabled" : "disabled"); m_dmrNetwork = new CDMRIPSC(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, rssi); unsigned int rxFrequency = m_conf.getRxFrequency(); unsigned int txFrequency = m_conf.getTxFrequency(); unsigned int power = m_conf.getPower(); unsigned int colorCode = m_conf.getDMRColorCode(); 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("Info Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); LogInfo(" RX Frequency: %uHz", rxFrequency); LogInfo(" TX Frequency: %uHz", txFrequency); LogInfo(" Power: %uW", power); 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); bool ret = m_dmrNetwork->open(); if (!ret) { delete m_dmrNetwork; m_dmrNetwork = NULL; return false; } m_dmrNetwork->enable(true); return true; } bool CMMDVMHost::createYSFNetwork() { std::string myAddress = m_conf.getFusionNetworkMyAddress(); unsigned int myPort = m_conf.getFusionNetworkMyPort(); std::string gwyAddress = m_conf.getFusionNetworkGwyAddress(); unsigned int gwyPort = m_conf.getFusionNetworkGwyPort(); bool debug = m_conf.getFusionNetworkDebug(); LogInfo("System Fusion Network Parameters"); LogInfo(" Local Address: %s", myAddress.c_str()); LogInfo(" Local Port: %u", myPort); LogInfo(" Gateway Address: %s", gwyAddress.c_str()); LogInfo(" Gateway Port: %u", gwyPort); m_ysfNetwork = new CYSFNetwork(myAddress, myPort, gwyAddress, gwyPort, m_callsign, debug); bool ret = m_ysfNetwork->open(); if (!ret) { delete m_ysfNetwork; m_ysfNetwork = NULL; return false; } m_ysfNetwork->enable(true); return true; } void CMMDVMHost::readParams() { m_dstarEnabled = m_conf.getDStarEnabled(); m_dmrEnabled = m_conf.getDMREnabled(); m_ysfEnabled = m_conf.getFusionEnabled(); m_duplex = m_conf.getDuplex(); m_callsign = m_conf.getCallsign(); m_timeout = m_conf.getTimeout(); m_rfModeHang = m_conf.getRFModeHang(); m_netModeHang = m_conf.getNetModeHang(); LogInfo("General Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); LogInfo(" Timeout: %us", m_timeout); LogInfo(" RF Mode Hang: %us", m_rfModeHang); LogInfo(" Net Mode Hang: %us", m_netModeHang); LogInfo(" D-Star: %s", m_dstarEnabled ? "enabled" : "disabled"); LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled"); } void CMMDVMHost::createDisplay() { std::string type = m_conf.getDisplay(); unsigned int dmrid = m_conf.getDMRId(); LogInfo("Display Parameters"); LogInfo(" Type: %s", type.c_str()); if (type == "TFT Serial") { std::string port = m_conf.getTFTSerialPort(); unsigned int brightness = m_conf.getTFTSerialBrightness(); LogInfo(" Port: %s", port.c_str()); LogInfo(" Brightness: %u", brightness); m_display = new CTFTSerial(m_callsign, dmrid, port, brightness); } else if (type == "Nextion") { std::string port = m_conf.getNextionPort(); unsigned int brightness = m_conf.getNextionBrightness(); bool displayClock = m_conf.getNextionDisplayClock(); bool utc = m_conf.getNextionUTC(); unsigned int idleBrightness = m_conf.getNextionIdleBrightness(); LogInfo(" Port: %s", port.c_str()); LogInfo(" Brightness: %u", brightness); LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); if (displayClock) LogInfo(" Display UTC: %s", utc ? "yes" : "no"); LogInfo(" Idle Brightness: %u", idleBrightness); m_display = new CNextion(m_callsign, dmrid, port, brightness, displayClock, utc, idleBrightness); #if defined(HD44780) } else if (type == "HD44780") { unsigned int rows = m_conf.getHD44780Rows(); unsigned int columns = m_conf.getHD44780Columns(); std::vector pins = m_conf.getHD44780Pins(); unsigned int i2cAddress = m_conf.getHD44780i2cAddress(); bool pwm = m_conf.getHD44780PWM(); unsigned int pwmPin = m_conf.getHD44780PWMPin(); unsigned int pwmBright = m_conf.getHD44780PWMBright(); unsigned int pwmDim = m_conf.getHD44780PWMDim(); bool displayClock = m_conf.getHD44780DisplayClock(); bool utc = m_conf.getHD44780UTC(); if (pins.size() == 6U) { LogInfo(" Rows: %u", rows); LogInfo(" Columns: %u", columns); #if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY) LogInfo(" Device Address: %#x", i2cAddress); #else LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U)); #endif LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no"); if (pwm) { LogInfo(" PWM Pin: %u", pwmPin); LogInfo(" PWM Bright: %u", pwmBright); LogInfo(" PWM Dim: %u", pwmDim); } LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); if (displayClock) LogInfo(" Display UTC: %s", utc ? "yes" : "no"); m_display = new CHD44780(rows, columns, m_callsign, dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, m_duplex); } #endif #if defined(OLED) } else if (type == "OLED") { unsigned char displayType = m_conf.getOLEDType(); unsigned char displayBrightness = m_conf.getOLEDBrightness(); unsigned char displayInvert = m_conf.getOLEDInvert(); m_display = new COLED(displayType, displayBrightness, displayInvert); #endif } else { m_display = new CNullDisplay; } bool ret = m_display->open(); if (!ret) { delete m_display; m_display = new CNullDisplay; } } void CMMDVMHost::setMode(unsigned char mode) { assert(m_modem != NULL); assert(m_display != NULL); switch (mode) { case MODE_DSTAR: if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); if (m_ysfNetwork != NULL) m_ysfNetwork->enable(false); m_modem->setMode(MODE_DSTAR); m_mode = MODE_DSTAR; m_modeTimer.start(); break; case MODE_DMR: if (m_dstarNetwork != NULL) m_dstarNetwork->enable(false); if (m_ysfNetwork != NULL) m_ysfNetwork->enable(false); m_modem->setMode(MODE_DMR); if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); } m_mode = MODE_DMR; m_modeTimer.start(); break; case MODE_YSF: if (m_dstarNetwork != NULL) m_dstarNetwork->enable(false); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); m_modem->setMode(MODE_YSF); m_mode = MODE_YSF; m_modeTimer.start(); break; case MODE_LOCKOUT: LogMessage("Mode set to Lockout"); 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_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(); break; case MODE_ERROR: LogMessage("Mode set to Error"); 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_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(); break; default: if (m_dstarNetwork != NULL) m_dstarNetwork->enable(true); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(true); if (m_ysfNetwork != NULL) m_ysfNetwork->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_mode == MODE_LOCKOUT) { m_modem->sendCWId(m_callsign); m_cwIdTimer.start(); } m_display->setIdle(); m_mode = MODE_IDLE; m_modeTimer.stop(); break; } }