From 2e654580083d0d8df51fb92ad11c1eacf99f4d74 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 8 Dec 2023 13:44:01 -0500 Subject: [PATCH] refactor Host.cpp into more partial clas files for better code organization; --- src/host/Host.Config.cpp | 787 +++++++++++++++++++++++++++++++++++++++ src/host/Host.cpp | 748 ------------------------------------- src/host/Host.h | 21 +- 3 files changed, 798 insertions(+), 758 deletions(-) create mode 100644 src/host/Host.Config.cpp diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp new file mode 100644 index 00000000..ecd4f04f --- /dev/null +++ b/src/host/Host.Config.cpp @@ -0,0 +1,787 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2017-2023 by Bryan Biedenkapp N2PLL +* +* 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 "Defines.h" +#include "modem/port/ModemNullPort.h" +#include "modem/port/UARTPort.h" +#include "modem/port/PseudoPTYPort.h" +#include "modem/port/UDPPort.h" +#include "network/UDPSocket.h" +#include "host/Host.h" +#include "HostMain.h" + +using namespace network; +using namespace modem; +using namespace lookups; + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/// +/// Reads basic configuration parameters from the YAML configuration file. +/// +bool Host::readParams() +{ + yaml::Node modemConf = m_conf["system"]["modem"]; + + yaml::Node modemProtocol = modemConf["protocol"]; + std::string portType = modemProtocol["type"].as("null"); + + yaml::Node udpProtocol = modemProtocol["udp"]; + std::string udpMode = udpProtocol["mode"].as("master"); + + bool udpMasterMode = false; + std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); + if ((portType == UART_PORT || portType == PTY_PORT) && g_remoteModemMode) { + udpMasterMode = true; + } + + yaml::Node protocolConf = m_conf["protocols"]; +#if defined(ENABLE_DMR) + m_dmrEnabled = protocolConf["dmr"]["enable"].as(false); +#else + m_dmrEnabled = false; // hardcode to false when no DMR support is compiled in +#endif // defined(ENABLE_DMR) +#if defined(ENABLE_P25) + m_p25Enabled = protocolConf["p25"]["enable"].as(false); +#else + m_p25Enabled = false; // hardcode to false when no P25 support is compiled in +#endif // defined(ENABLE_P25) +#if defined(ENABLE_NXDN) + m_nxdnEnabled = protocolConf["nxdn"]["enable"].as(false); +#else + m_nxdnEnabled = false; // hardcode to false when no NXDN support is compiled in +#endif // defined(ENABLE_NXDN) + + yaml::Node systemConf = m_conf["system"]; + m_duplex = systemConf["duplex"].as(true); + bool simplexSameFreq = systemConf["simplexSameFrequency"].as(false); + + m_timeout = systemConf["timeout"].as(120U); + m_rfModeHang = systemConf["rfModeHang"].as(10U); + m_rfTalkgroupHang = systemConf["rfTalkgroupHang"].as(10U); + m_netModeHang = systemConf["netModeHang"].as(3U); + if (!systemConf["modeHang"].isNone()) { + m_rfModeHang = m_netModeHang = systemConf["modeHang"].as(); + } + + m_activeTickDelay = (uint8_t)systemConf["activeTickDelay"].as(5U); + if (m_activeTickDelay < 1U) + m_activeTickDelay = 1U; + m_idleTickDelay = (uint8_t)systemConf["idleTickDelay"].as(5U); + if (m_idleTickDelay < 1U) + m_idleTickDelay = 1U; + + m_identity = systemConf["identity"].as(); + m_fixedMode = systemConf["fixedMode"].as(false); + + if (m_identity.length() > 8) { + std::string identity = m_identity; + m_identity = identity.substr(0, 8); + + ::LogWarning(LOG_HOST, "System Identity \"%s\" is too long; truncating to 8 characters, \"%s\".", identity.c_str(), m_identity.c_str()); + } + + int8_t lto = (int8_t)systemConf["localTimeOffset"].as(0); + + removeLockFile(); + + LogInfo("General Parameters"); + if (!udpMasterMode) { + LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); + LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); + LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); + LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); + if (!m_duplex) { + LogInfo(" Simplex Same Frequency: %s", simplexSameFreq ? "yes" : "no"); + } + LogInfo(" Active Tick Delay: %ums", m_activeTickDelay); + LogInfo(" Idle Tick Delay: %ums", m_idleTickDelay); + LogInfo(" Timeout: %us", m_timeout); + LogInfo(" RF Mode Hang: %us", m_rfModeHang); + LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang); + LogInfo(" Net Mode Hang: %us", m_netModeHang); + LogInfo(" Identity: %s", m_identity.c_str()); + LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); + LogInfo(" Lock Filename: %s", g_lockFile.c_str()); + LogInfo(" Local Time Offset: %dh", lto); + + yaml::Node systemInfo = systemConf["info"]; + m_latitude = systemInfo["latitude"].as(0.0F); + m_longitude = systemInfo["longitude"].as(0.0F); + m_height = systemInfo["height"].as(0); + m_power = systemInfo["power"].as(0U); + m_location = systemInfo["location"].as(); + + LogInfo("System Info Parameters"); + LogInfo(" Latitude: %fdeg N", m_latitude); + LogInfo(" Longitude: %fdeg E", m_longitude); + LogInfo(" Height: %um", m_height); + LogInfo(" Power: %uW", m_power); + LogInfo(" Location: \"%s\"", m_location.c_str()); + + // try to load bandplan identity table + std::string idenLookupFile = systemConf["iden_table"]["file"].as(); + uint32_t idenReloadTime = systemConf["iden_table"]["time"].as(0U); + + if (idenLookupFile.length() <= 0U) { + ::LogError(LOG_HOST, "No bandplan identity table? This must be defined!"); + return false; + } + + LogInfo("Iden Table Lookups"); + LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None"); + if (idenReloadTime > 0U) + LogInfo(" Reload: %u mins", idenReloadTime); + + m_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime); + m_idenTable->read(); + + /* + ** Channel Configuration + */ + yaml::Node rfssConfig = systemConf["config"]; + m_channelId = (uint8_t)rfssConfig["channelId"].as(0U); + if (m_channelId > 15U) { // clamp to 15 + m_channelId = 15U; + } + + IdenTable entry = m_idenTable->find(m_channelId); + if (entry.baseFrequency() == 0U) { + ::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId); + return false; + } + + m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as("1").c_str(), NULL, 16); + if (m_channelNo == 0U) { // clamp to 1 + m_channelNo = 1U; + } + if (m_channelNo > 4095U) { // clamp to 4095 + m_channelNo = 4095U; + } + + if (entry.txOffsetMhz() == 0U) { + ::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId); + return false; + } + + uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125); + float calcTxOffset = entry.txOffsetMhz() * 1000000; + + m_txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo))); + m_rxFrequency = (uint32_t)(m_txFrequency + calcTxOffset); + + if (calcTxOffset < 0.0f && m_rxFrequency < entry.baseFrequency()) { + ::LogWarning(LOG_HOST, "Channel Id %u Channel No $%04X has an invalid frequency. Rx Frequency (%u) is less then the base frequency (%u), this may result in incorrect trunking behavior.", m_channelId, m_channelNo, + m_rxFrequency, entry.baseFrequency()); + } + + if (!m_duplex && simplexSameFreq) { + m_rxFrequency = m_txFrequency; + } + + /* + ** Control Channel + */ + { + yaml::Node controlCh = rfssConfig["controlCh"]; + + std::string restApiAddress = controlCh["restAddress"].as(""); + uint16_t restApiPort = (uint16_t)controlCh["restPort"].as(REST_API_DEFAULT_PORT); + std::string restApiPassword = controlCh["restPassword"].as(); + + VoiceChData data = VoiceChData(m_channelId, m_channelNo, restApiAddress, restApiPort, restApiPassword); + m_controlChData = data; + + if (!m_controlChData.address().empty() && m_controlChData.port() > 0) { + ::LogInfoEx(LOG_HOST, "Control Channel REST API Address %s:%u", m_controlChData.address().c_str(), m_controlChData.port()); + } else { + ::LogInfoEx(LOG_HOST, "No Control Channel REST API Configured, CC notify disabled"); + } + } + + /* + ** Voice Channels + */ + yaml::Node& voiceChList = rfssConfig["voiceChNo"]; + + if (voiceChList.size() == 0U) { + ::LogError(LOG_HOST, "No voice channel list defined!"); + return false; + } + + for (size_t i = 0; i < voiceChList.size(); i++) { + yaml::Node& channel = voiceChList[i]; + + uint8_t chId = (uint8_t)channel["channelId"].as(255U); + + // special case default handling for if the channelId field is missing from the + // configuration + if (chId == 255U) { + chId = m_channelId; + } + + if (chId > 15U) { // clamp to 15 + chId = 15U; + } + + uint32_t chNo = (uint32_t)::strtoul(channel["channelNo"].as("1").c_str(), NULL, 16); + if (chNo == 0U) { // clamp to 1 + chNo = 1U; + } + if (chNo > 4095U) { // clamp to 4095 + chNo = 4095U; + } + + std::string restApiAddress = channel["restAddress"].as("127.0.0.1"); + uint16_t restApiPort = (uint16_t)channel["restPort"].as(REST_API_DEFAULT_PORT); + std::string restApiPassword = channel["restPassword"].as(); + + ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u", chId, chNo, restApiAddress.c_str(), restApiPort); + + VoiceChData data = VoiceChData(chId, chNo, restApiAddress, restApiPort, restApiPassword); + m_voiceChData[chNo] = data; + m_voiceChNo.push_back(chNo); + } + + std::string strVoiceChNo = ""; + for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { + uint32_t chNo = ::atoi(std::to_string(*it).c_str()); + ::lookups::VoiceChData voiceChData = m_voiceChData[chNo]; + + char hexStr[29]; + + ::sprintf(hexStr, "$%01X.%01X (%u.%u)", voiceChData.chId(), chNo, voiceChData.chId(), chNo); + + strVoiceChNo.append(std::string(hexStr)); + strVoiceChNo.append(","); + } + strVoiceChNo.erase(strVoiceChNo.find_last_of(",")); + + /* + ** Site Parameters + */ + m_siteId = (uint8_t)::strtoul(rfssConfig["siteId"].as("1").c_str(), NULL, 16); + m_siteId = p25::P25Utils::siteId(m_siteId); + + m_dmrColorCode = rfssConfig["colorCode"].as(2U); + m_dmrColorCode = dmr::DMRUtils::colorCode(m_dmrColorCode); + + m_dmrNetId = (uint32_t)::strtoul(rfssConfig["dmrNetId"].as("1").c_str(), NULL, 16); + m_dmrNetId = dmr::DMRUtils::netId(m_dmrNetId, dmr::SITE_MODEL_TINY); + + m_p25NAC = (uint32_t)::strtoul(rfssConfig["nac"].as("293").c_str(), NULL, 16); + m_p25NAC = p25::P25Utils::nac(m_p25NAC); + + uint32_t p25TxNAC = (uint32_t)::strtoul(rfssConfig["txNAC"].as("F7E").c_str(), NULL, 16); + if (p25TxNAC == m_p25NAC) { + LogWarning(LOG_HOST, "Only use txNAC when split NAC operations are needed. nac and txNAC should not be the same!"); + } + + m_p25NetId = (uint32_t)::strtoul(rfssConfig["netId"].as("BB800").c_str(), NULL, 16); + m_p25NetId = p25::P25Utils::netId(m_p25NetId); + if (m_p25NetId == 0xBEE00) { + ::fatal("error 4\n"); + } + + m_sysId = (uint32_t)::strtoul(rfssConfig["sysId"].as("001").c_str(), NULL, 16); + m_sysId = p25::P25Utils::sysId(m_sysId); + + m_p25RfssId = (uint8_t)::strtoul(rfssConfig["rfssId"].as("1").c_str(), NULL, 16); + m_p25RfssId = p25::P25Utils::rfssId(m_p25RfssId); + + m_nxdnRAN = rfssConfig["ran"].as(1U); + + m_authoritative = rfssConfig["authoritative"].as(true); + + LogInfo("System Config Parameters"); + LogInfo(" Authoritative: %s", m_authoritative ? "yes" : "no"); + if (m_authoritative) { + m_supervisor = rfssConfig["supervisor"].as(false); + LogInfo(" Supervisor: %s", m_supervisor ? "yes" : "no"); + } + LogInfo(" RX Frequency: %uHz", m_rxFrequency); + LogInfo(" TX Frequency: %uHz", m_txFrequency); + LogInfo(" Base Frequency: %uHz", entry.baseFrequency()); + LogInfo(" TX Offset: %fMHz", entry.txOffsetMhz()); + LogInfo(" Bandwidth: %fKHz", entry.chBandwidthKhz()); + LogInfo(" Channel Spacing: %fKHz", entry.chSpaceKhz()); + LogInfo(" Channel Id: %u", m_channelId); + LogInfo(" Channel No.: $%04X (%u)", m_channelNo, m_channelNo); + LogInfo(" Voice Channel No(s).: %s", strVoiceChNo.c_str()); + LogInfo(" Site Id: $%02X", m_siteId); + LogInfo(" System Id: $%03X", m_sysId); + LogInfo(" DMR Color Code: %u", m_dmrColorCode); + LogInfo(" DMR Network Id: $%05X", m_dmrNetId); + LogInfo(" P25 NAC: $%03X", m_p25NAC); + + if (p25TxNAC != 0xF7EU && p25TxNAC != m_p25NAC) { + LogInfo(" P25 Tx NAC: $%03X", p25TxNAC); + } + + LogInfo(" P25 Network Id: $%05X", m_p25NetId); + LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId); + LogInfo(" NXDN RAN: %u", m_nxdnRAN); + + if (!m_authoritative) { + m_supervisor = false; + LogWarning(LOG_HOST, "Host is non-authoritative! This requires REST API to handle permit TG for VCs and grant TG for CCs!"); + } + } + else { + LogInfo(" Modem Remote Control: yes"); + } + + return true; +} + +/// +/// Initializes the modem DSP. +/// +bool Host::createModem() +{ + yaml::Node protocolConf = m_conf["protocols"]; + + yaml::Node dmrProtocol = protocolConf["dmr"]; + uint32_t dmrQueueSize = dmrProtocol["queueSize"].as(24U); + + // clamp queue size to no less then 24 and no greater the 100 + if (dmrQueueSize < 24U) { + LogWarning(LOG_HOST, "DMR queue size must be greater then 24 frames, defaulting to 24 frames!"); + dmrQueueSize = 24U; + } + if (dmrQueueSize > 100U) { + LogWarning(LOG_HOST, "DMR queue size must be less then 100 frames, defaulting to 100 frames!"); + dmrQueueSize = 100U; + } + if (dmrQueueSize > 60U) { + LogWarning(LOG_HOST, "DMR queue size is excessive, >60 frames!"); + } + + m_dmrQueueSizeBytes = dmrQueueSize * (dmr::DMR_FRAME_LENGTH_BYTES * 5U); + + yaml::Node p25Protocol = protocolConf["p25"]; + uint32_t p25QueueSize = p25Protocol["queueSize"].as(12U); + + // clamp queue size to no less then 12 and no greater the 100 frames + if (p25QueueSize < 12U) { + LogWarning(LOG_HOST, "P25 queue size must be greater then 12 frames, defaulting to 12 frames!"); + p25QueueSize = 12U; + } + if (p25QueueSize > 50U) { + LogWarning(LOG_HOST, "P25 queue size must be less then 50 frames, defaulting to 50 frames!"); + p25QueueSize = 50U; + } + if (p25QueueSize > 30U) { + LogWarning(LOG_HOST, "P25 queue size is excessive, >30 frames!"); + } + + m_p25QueueSizeBytes = p25QueueSize * p25::P25_LDU_FRAME_LENGTH_BYTES; + + yaml::Node nxdnProtocol = protocolConf["nxdn"]; + uint32_t nxdnQueueSize = nxdnProtocol["queueSize"].as(31U); + + // clamp queue size to no less then 31 and no greater the 50 frames + if (nxdnQueueSize < 31U) { + LogWarning(LOG_HOST, "NXDN queue size must be greater then 31 frames, defaulting to 31 frames!"); + nxdnQueueSize = 31U; + } + if (nxdnQueueSize > 50U) { + LogWarning(LOG_HOST, "NXDN queue size must be less then 50 frames, defaulting to 50 frames!"); + nxdnQueueSize = 50U; + } + + m_nxdnQueueSizeBytes = nxdnQueueSize * nxdn::NXDN_FRAME_LENGTH_BYTES; + + yaml::Node modemConf = m_conf["system"]["modem"]; + + yaml::Node modemProtocol = modemConf["protocol"]; + std::string portType = modemProtocol["type"].as("null"); + yaml::Node uartProtocol = modemProtocol["uart"]; + std::string uartPort = uartProtocol["port"].as(); + uint32_t uartSpeed = uartProtocol["speed"].as(115200); + + bool rxInvert = modemConf["rxInvert"].as(false); + bool txInvert = modemConf["txInvert"].as(false); + bool pttInvert = modemConf["pttInvert"].as(false); + bool dcBlocker = modemConf["dcBlocker"].as(true); + bool cosLockout = modemConf["cosLockout"].as(false); + uint8_t fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U); + uint8_t dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); + uint8_t p25CorrCount = (uint8_t)modemConf["p25CorrCount"].as(4U); + int rxDCOffset = modemConf["rxDCOffset"].as(0); + int txDCOffset = modemConf["txDCOffset"].as(0); + + yaml::Node hotspotParams = modemConf["hotspot"]; + + int dmrDiscBWAdj = hotspotParams["dmrDiscBWAdj"].as(0); + int p25DiscBWAdj = hotspotParams["p25DiscBWAdj"].as(0); + int nxdnDiscBWAdj = hotspotParams["nxdnDiscBWAdj"].as(0); + int dmrPostBWAdj = hotspotParams["dmrPostBWAdj"].as(0); + int p25PostBWAdj = hotspotParams["p25PostBWAdj"].as(0); + int nxdnPostBWAdj = hotspotParams["nxdnPostBWAdj"].as(0); + ADF_GAIN_MODE adfGainMode = (ADF_GAIN_MODE)hotspotParams["adfGainMode"].as(0U); + bool afcEnable = hotspotParams["afcEnable"].as(false); + uint8_t afcKI = (uint8_t)hotspotParams["afcKI"].as(11U); + uint8_t afcKP = (uint8_t)hotspotParams["afcKP"].as(4U); + uint8_t afcRange = (uint8_t)hotspotParams["afcRange"].as(1U); + int rxTuning = hotspotParams["rxTuning"].as(0); + int txTuning = hotspotParams["txTuning"].as(0); + uint8_t rfPower = (uint8_t)hotspotParams["rfPower"].as(100U); + + yaml::Node repeaterParams = modemConf["repeater"]; + + int dmrSymLevel3Adj = repeaterParams["dmrSymLvl3Adj"].as(0); + int dmrSymLevel1Adj = repeaterParams["dmrSymLvl1Adj"].as(0); + int p25SymLevel3Adj = repeaterParams["p25SymLvl3Adj"].as(0); + int p25SymLevel1Adj = repeaterParams["p25SymLvl1Adj"].as(0); + int nxdnSymLevel3Adj = repeaterParams["nxdnSymLvl3Adj"].as(0); + int nxdnSymLevel1Adj = repeaterParams["nxdnSymLvl1Adj"].as(0); + + yaml::Node softpotParams = modemConf["softpot"]; + + uint8_t rxCoarse = (uint8_t)softpotParams["rxCoarse"].as(127U); + uint8_t rxFine = (uint8_t)softpotParams["rxFine"].as(127U); + uint8_t txCoarse = (uint8_t)softpotParams["txCoarse"].as(127U); + uint8_t txFine = (uint8_t)softpotParams["txFine"].as(127U); + uint8_t rssiCoarse = (uint8_t)softpotParams["rssiCoarse"].as(127U); + uint8_t rssiFine = (uint8_t)softpotParams["rssiFine"].as(127U); + + uint16_t dmrFifoLength = (uint16_t)modemConf["dmrFifoLength"].as(DMR_TX_BUFFER_LEN); + uint16_t p25FifoLength = (uint16_t)modemConf["p25FifoLength"].as(P25_TX_BUFFER_LEN); + uint16_t nxdnFifoLength = (uint16_t)modemConf["nxdnFifoLength"].as(NXDN_TX_BUFFER_LEN); + + float rxLevel = modemConf["rxLevel"].as(50.0F); + float cwIdTXLevel = modemConf["cwIdTxLevel"].as(50.0F); + float dmrTXLevel = modemConf["dmrTxLevel"].as(50.0F); + float p25TXLevel = modemConf["p25TxLevel"].as(50.0F); + float nxdnTXLevel = modemConf["nxdnTxLevel"].as(50.0F); + if (!modemConf["txLevel"].isNone()) { + cwIdTXLevel = dmrTXLevel = p25TXLevel = nxdnTXLevel = modemConf["txLevel"].as(50.0F); + } + bool disableOFlowReset = modemConf["disableOFlowReset"].as(false); + bool ignoreModemConfigArea = modemConf["ignoreModemConfigArea"].as(false); + bool dumpModemStatus = modemConf["dumpModemStatus"].as(false); + bool trace = modemConf["trace"].as(false); + bool debug = modemConf["debug"].as(false); + + if (rfPower == 0U) { // clamp to 1 + rfPower = 1U; + } + if (rfPower > 100U) { // clamp to 100 + rfPower = 100U; + } + + LogInfo("Modem Parameters"); + LogInfo(" Port Type: %s", portType.c_str()); + + port::IModemPort* modemPort = nullptr; + std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); + if (portType == NULL_PORT) { + modemPort = new port::ModemNullPort(); + } + else if (portType == UART_PORT || portType == PTY_PORT) { + port::SERIAL_SPEED serialSpeed = port::SERIAL_115200; + switch (uartSpeed) { + case 1200: + serialSpeed = port::SERIAL_1200; + break; + case 2400: + serialSpeed = port::SERIAL_2400; + break; + case 4800: + serialSpeed = port::SERIAL_4800; + break; + case 9600: + serialSpeed = port::SERIAL_9600; + break; + case 19200: + serialSpeed = port::SERIAL_19200; + break; + case 38400: + serialSpeed = port::SERIAL_38400; + break; + case 76800: + serialSpeed = port::SERIAL_76800; + break; + case 230400: + serialSpeed = port::SERIAL_230400; + break; + case 460800: + serialSpeed = port::SERIAL_460800; + break; + default: + LogWarning(LOG_HOST, "Unsupported serial speed %u, defaulting to %u", uartSpeed, port::SERIAL_115200); + uartSpeed = 115200; + case 115200: + break; + } + + if (portType == PTY_PORT) { + modemPort = new port::UARTPort(uartPort, serialSpeed, false); + LogInfo(" PTY Port: %s", uartPort.c_str()); + LogInfo(" PTY Speed: %u", uartSpeed); + } + else { + modemPort = new port::UARTPort(uartPort, serialSpeed, true); + LogInfo(" UART Port: %s", uartPort.c_str()); + LogInfo(" UART Speed: %u", uartSpeed); + } + } + else { + LogError(LOG_HOST, "Invalid protocol port type, %s!", portType.c_str()); + return false; + } + + if (g_remoteModemMode) { + if (portType == UART_PORT || portType == PTY_PORT) { + m_modemRemotePort = new port::UDPPort(g_remoteAddress, g_remotePort); + m_modemRemote = true; + ignoreModemConfigArea = true; + + } + else { + delete modemPort; + modemPort = new port::UDPPort(g_remoteAddress, g_remotePort); + m_modemRemote = false; + } + + LogInfo(" UDP Mode: %s", m_modemRemote ? "master" : "peer"); + LogInfo(" UDP Address: %s", g_remoteAddress.c_str()); + LogInfo(" UDP Port: %u", g_remotePort); + } + + if (!m_modemRemote) { + LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); + LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); + LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); + LogInfo(" DC Blocker: %s", dcBlocker ? "yes" : "no"); + LogInfo(" COS Lockout: %s", cosLockout ? "yes" : "no"); + LogInfo(" FDMA Preambles: %u (%.1fms)", fdmaPreamble, float(fdmaPreamble) * 0.2222F); + LogInfo(" DMR RX Delay: %u (%.1fms)", dmrRxDelay, float(dmrRxDelay) * 0.0416666F); + LogInfo(" P25 Corr. Count: %u (%.1fms)", p25CorrCount, float(p25CorrCount) * 0.667F); + LogInfo(" RX DC Offset: %d", rxDCOffset); + LogInfo(" TX DC Offset: %d", txDCOffset); + LogInfo(" RX Tuning Offset: %dhz", rxTuning); + LogInfo(" TX Tuning Offset: %dhz", txTuning); + LogInfo(" RX Effective Frequency: %uhz", m_rxFrequency + rxTuning); + LogInfo(" TX Effective Frequency: %uhz", m_txFrequency + txTuning); + LogInfo(" RX Coarse: %u, Fine: %u", rxCoarse, rxFine); + LogInfo(" TX Coarse: %u, Fine: %u", txCoarse, txFine); + LogInfo(" RSSI Coarse: %u, Fine: %u", rssiCoarse, rssiFine); + LogInfo(" RF Power Level: %u", rfPower); + LogInfo(" RX Level: %.1f%%", rxLevel); + LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); + LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); + LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); + LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); + LogInfo(" Disable Overflow Reset: %s", disableOFlowReset ? "yes" : "no"); + LogInfo(" DMR Queue Size: %u (%u bytes)", dmrQueueSize, m_dmrQueueSizeBytes); + LogInfo(" P25 Queue Size: %u (%u bytes)", p25QueueSize, m_p25QueueSizeBytes); + LogInfo(" NXDN Queue Size: %u (%u bytes)", nxdnQueueSize, m_nxdnQueueSizeBytes); + LogInfo(" DMR FIFO Size: %u bytes", dmrFifoLength); + LogInfo(" P25 FIFO Size: %u bytes", p25FifoLength); + LogInfo(" NXDN FIFO Size: %u bytes", nxdnFifoLength); + + if (ignoreModemConfigArea) { + LogInfo(" Ignore Modem Configuration Area: yes"); + } + + if (dumpModemStatus) { + LogInfo(" Dump Modem Status: yes"); + } + } + + if (debug) { + LogInfo(" Debug: yes"); + } + + m_modem = new Modem(modemPort, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, + m_dmrQueueSizeBytes, m_p25QueueSizeBytes, m_nxdnQueueSizeBytes, disableOFlowReset, ignoreModemConfigArea, dumpModemStatus, trace, debug); + if (!m_modemRemote) { + m_modem->setModeParams(m_dmrEnabled, m_p25Enabled, m_nxdnEnabled); + m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel, nxdnTXLevel); + m_modem->setSymbolAdjust(dmrSymLevel3Adj, dmrSymLevel1Adj, p25SymLevel3Adj, p25SymLevel1Adj, nxdnSymLevel3Adj, nxdnSymLevel1Adj); + m_modem->setDCOffsetParams(txDCOffset, rxDCOffset); + m_modem->setRFParams(m_rxFrequency, m_txFrequency, rxTuning, txTuning, rfPower, dmrDiscBWAdj, p25DiscBWAdj, nxdnDiscBWAdj, dmrPostBWAdj, + p25PostBWAdj, nxdnPostBWAdj, adfGainMode, afcEnable, afcKI, afcKP, afcRange); + m_modem->setSoftPot(rxCoarse, rxFine, txCoarse, txFine, rssiCoarse, rssiFine); + m_modem->setDMRColorCode(m_dmrColorCode); + m_modem->setP25NAC(m_p25NAC); + } + + if (m_modemRemote) { + m_modem->setOpenHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemOpen, this)); + m_modem->setCloseHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemClose, this)); + m_modem->setResponseHandler(MODEM_RESP_HANDLER_BIND(Host::rmtPortModemHandler, this)); + } + + bool ret = m_modem->open(); + if (!ret) { + delete m_modem; + m_modem = nullptr; + return false; + } + + m_modem->setFifoLength(dmrFifoLength, p25FifoLength, nxdnFifoLength); + + // are we on a protocol version older then 3? + if (m_modem->getVersion() < 3U) { + if (m_nxdnEnabled) { + ::LogError(LOG_HOST, "NXDN is not supported on legacy firmware."); + return false; + } + } + + return true; +} + +/// +/// Initializes network connectivity. +/// +bool Host::createNetwork() +{ + yaml::Node networkConf = m_conf["network"]; + bool netEnable = networkConf["enable"].as(false); + bool restApiEnable = networkConf["restEnable"].as(false); + + // dump out if both networking and REST API are disabled + if (!netEnable && !restApiEnable) { + return true; + } + + std::string address = networkConf["address"].as(); + uint16_t port = (uint16_t)networkConf["port"].as(TRAFFIC_DEFAULT_PORT); + uint16_t local = (uint16_t)networkConf["local"].as(0U); + std::string restApiAddress = networkConf["restAddress"].as("127.0.0.1"); + uint16_t restApiPort = (uint16_t)networkConf["restPort"].as(REST_API_DEFAULT_PORT); + std::string restApiPassword = networkConf["restPassword"].as(); + bool restApiDebug = networkConf["restDebug"].as(false); + uint32_t id = networkConf["id"].as(1000U); + uint32_t jitter = networkConf["talkgroupHang"].as(360U); + std::string password = networkConf["password"].as(); + bool slot1 = networkConf["slot1"].as(true); + bool slot2 = networkConf["slot2"].as(true); + bool allowActivityTransfer = networkConf["allowActivityTransfer"].as(false); + bool allowDiagnosticTransfer = networkConf["allowDiagnosticTransfer"].as(false); + bool updateLookup = networkConf["updateLookups"].as(false); + bool debug = networkConf["debug"].as(false); + + if (id > 999999999U) { + ::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999."); + return false; + } + + if (restApiPassword.length() > 64) { + std::string password = restApiPassword; + restApiPassword = password.substr(0, 64); + + ::LogWarning(LOG_HOST, "REST API password is too long; truncating to the first 64 characters."); + } + + if (restApiPassword.empty() && restApiEnable) { + ::LogWarning(LOG_HOST, "REST API password not provided; REST API disabled."); + restApiEnable = false; + } + + IdenTable entry = m_idenTable->find(m_channelId); + + LogInfo("Network Parameters"); + LogInfo(" Enabled: %s", netEnable ? "yes" : "no"); + if (netEnable) { + LogInfo(" Peer ID: %u", id); + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + if (local > 0U) + LogInfo(" Local: %u", local); + else + LogInfo(" Local: random"); + LogInfo(" DMR Jitter: %ums", jitter); + LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); + LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); + LogInfo(" Allow Activity Log Transfer: %s", allowActivityTransfer ? "yes" : "no"); + LogInfo(" Allow Diagnostic Log Transfer: %s", allowDiagnosticTransfer ? "yes" : "no"); + LogInfo(" Update Lookups: %s", updateLookup ? "yes" : "no"); + + if (debug) { + LogInfo(" Debug: yes"); + } + } + LogInfo(" REST API Enabled: %s", restApiEnable ? "yes" : "no"); + if (restApiEnable) { + LogInfo(" REST API Address: %s", restApiAddress.c_str()); + LogInfo(" REST API Port: %u", restApiPort); + + if (restApiDebug) { + LogInfo(" REST API Debug: yes"); + } + } + + // initialize networking + if (netEnable) { + m_network = new Network(address, port, local, id, password, m_duplex, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup); + + m_network->setLookups(m_ridLookup, m_tidLookup); + m_network->setMetadata(m_identity, m_rxFrequency, m_txFrequency, entry.txOffsetMhz(), entry.chBandwidthKhz(), m_channelId, m_channelNo, + m_power, m_latitude, m_longitude, m_height, m_location); + if (restApiEnable) { + m_network->setRESTAPIData(restApiPassword, restApiPort); + } + + m_network->enable(true); + bool ret = m_network->open(); + if (!ret) { + delete m_network; + m_network = nullptr; + LogError(LOG_HOST, "failed to initialize traffic networking!"); + return false; + } + + ::LogSetNetwork(m_network); + } + + // initialize network remote command + if (restApiEnable) { + m_RESTAPI = new RESTAPI(restApiAddress, restApiPort, restApiPassword, this, restApiDebug); + m_RESTAPI->setLookups(m_ridLookup, m_tidLookup); + bool ret = m_RESTAPI->open(); + if (!ret) { + delete m_RESTAPI; + m_RESTAPI = nullptr; + LogError(LOG_HOST, "failed to initialize REST API networking! REST API will be unavailable!"); + // REST API failing isn't fatal -- we'll allow this to return normally + } + } + else { + m_RESTAPI = nullptr; + } + + return true; +} diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 660d74b4..73bbd7bf 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -32,11 +32,6 @@ #include "Defines.h" #include "dmr/DMRUtils.h" #include "p25/P25Utils.h" -#include "modem/port/ModemNullPort.h" -#include "modem/port/UARTPort.h" -#include "modem/port/PseudoPTYPort.h" -#include "modem/port/UDPPort.h" -#include "network/UDPSocket.h" #include "lookups/RSSIInterpolator.h" #include "host/Host.h" #include "HostMain.h" @@ -46,7 +41,6 @@ #include "ThreadFunc.h" #include "Utils.h" -using namespace network; using namespace modem; using namespace lookups; @@ -1469,748 +1463,6 @@ int Host::run() // Private Class Members // --------------------------------------------------------------------------- -/// -/// Reads basic configuration parameters from the YAML configuration file. -/// -bool Host::readParams() -{ - yaml::Node modemConf = m_conf["system"]["modem"]; - - yaml::Node modemProtocol = modemConf["protocol"]; - std::string portType = modemProtocol["type"].as("null"); - - yaml::Node udpProtocol = modemProtocol["udp"]; - std::string udpMode = udpProtocol["mode"].as("master"); - - bool udpMasterMode = false; - std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); - if ((portType == UART_PORT || portType == PTY_PORT) && g_remoteModemMode) { - udpMasterMode = true; - } - - yaml::Node protocolConf = m_conf["protocols"]; -#if defined(ENABLE_DMR) - m_dmrEnabled = protocolConf["dmr"]["enable"].as(false); -#else - m_dmrEnabled = false; // hardcode to false when no DMR support is compiled in -#endif // defined(ENABLE_DMR) -#if defined(ENABLE_P25) - m_p25Enabled = protocolConf["p25"]["enable"].as(false); -#else - m_p25Enabled = false; // hardcode to false when no P25 support is compiled in -#endif // defined(ENABLE_P25) -#if defined(ENABLE_NXDN) - m_nxdnEnabled = protocolConf["nxdn"]["enable"].as(false); -#else - m_nxdnEnabled = false; // hardcode to false when no NXDN support is compiled in -#endif // defined(ENABLE_NXDN) - - yaml::Node systemConf = m_conf["system"]; - m_duplex = systemConf["duplex"].as(true); - bool simplexSameFreq = systemConf["simplexSameFrequency"].as(false); - - m_timeout = systemConf["timeout"].as(120U); - m_rfModeHang = systemConf["rfModeHang"].as(10U); - m_rfTalkgroupHang = systemConf["rfTalkgroupHang"].as(10U); - m_netModeHang = systemConf["netModeHang"].as(3U); - if (!systemConf["modeHang"].isNone()) { - m_rfModeHang = m_netModeHang = systemConf["modeHang"].as(); - } - - m_activeTickDelay = (uint8_t)systemConf["activeTickDelay"].as(5U); - if (m_activeTickDelay < 1U) - m_activeTickDelay = 1U; - m_idleTickDelay = (uint8_t)systemConf["idleTickDelay"].as(5U); - if (m_idleTickDelay < 1U) - m_idleTickDelay = 1U; - - m_identity = systemConf["identity"].as(); - m_fixedMode = systemConf["fixedMode"].as(false); - - if (m_identity.length() > 8) { - std::string identity = m_identity; - m_identity = identity.substr(0, 8); - - ::LogWarning(LOG_HOST, "System Identity \"%s\" is too long; truncating to 8 characters, \"%s\".", identity.c_str(), m_identity.c_str()); - } - - int8_t lto = (int8_t)systemConf["localTimeOffset"].as(0); - - removeLockFile(); - - LogInfo("General Parameters"); - if (!udpMasterMode) { - LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); - LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); - LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); - LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); - if (!m_duplex) { - LogInfo(" Simplex Same Frequency: %s", simplexSameFreq ? "yes" : "no"); - } - LogInfo(" Active Tick Delay: %ums", m_activeTickDelay); - LogInfo(" Idle Tick Delay: %ums", m_idleTickDelay); - LogInfo(" Timeout: %us", m_timeout); - LogInfo(" RF Mode Hang: %us", m_rfModeHang); - LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang); - LogInfo(" Net Mode Hang: %us", m_netModeHang); - LogInfo(" Identity: %s", m_identity.c_str()); - LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); - LogInfo(" Lock Filename: %s", g_lockFile.c_str()); - LogInfo(" Local Time Offset: %dh", lto); - - yaml::Node systemInfo = systemConf["info"]; - m_latitude = systemInfo["latitude"].as(0.0F); - m_longitude = systemInfo["longitude"].as(0.0F); - m_height = systemInfo["height"].as(0); - m_power = systemInfo["power"].as(0U); - m_location = systemInfo["location"].as(); - - LogInfo("System Info Parameters"); - LogInfo(" Latitude: %fdeg N", m_latitude); - LogInfo(" Longitude: %fdeg E", m_longitude); - LogInfo(" Height: %um", m_height); - LogInfo(" Power: %uW", m_power); - LogInfo(" Location: \"%s\"", m_location.c_str()); - - // try to load bandplan identity table - std::string idenLookupFile = systemConf["iden_table"]["file"].as(); - uint32_t idenReloadTime = systemConf["iden_table"]["time"].as(0U); - - if (idenLookupFile.length() <= 0U) { - ::LogError(LOG_HOST, "No bandplan identity table? This must be defined!"); - return false; - } - - LogInfo("Iden Table Lookups"); - LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None"); - if (idenReloadTime > 0U) - LogInfo(" Reload: %u mins", idenReloadTime); - - m_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime); - m_idenTable->read(); - - /* - ** Channel Configuration - */ - yaml::Node rfssConfig = systemConf["config"]; - m_channelId = (uint8_t)rfssConfig["channelId"].as(0U); - if (m_channelId > 15U) { // clamp to 15 - m_channelId = 15U; - } - - IdenTable entry = m_idenTable->find(m_channelId); - if (entry.baseFrequency() == 0U) { - ::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId); - return false; - } - - m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as("1").c_str(), NULL, 16); - if (m_channelNo == 0U) { // clamp to 1 - m_channelNo = 1U; - } - if (m_channelNo > 4095U) { // clamp to 4095 - m_channelNo = 4095U; - } - - if (entry.txOffsetMhz() == 0U) { - ::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId); - return false; - } - - uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125); - float calcTxOffset = entry.txOffsetMhz() * 1000000; - - m_txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo))); - m_rxFrequency = (uint32_t)(m_txFrequency + calcTxOffset); - - if (calcTxOffset < 0.0f && m_rxFrequency < entry.baseFrequency()) { - ::LogWarning(LOG_HOST, "Channel Id %u Channel No $%04X has an invalid frequency. Rx Frequency (%u) is less then the base frequency (%u), this may result in incorrect trunking behavior.", m_channelId, m_channelNo, - m_rxFrequency, entry.baseFrequency()); - } - - if (!m_duplex && simplexSameFreq) { - m_rxFrequency = m_txFrequency; - } - - /* - ** Control Channel - */ - { - yaml::Node controlCh = rfssConfig["controlCh"]; - - std::string restApiAddress = controlCh["restAddress"].as(""); - uint16_t restApiPort = (uint16_t)controlCh["restPort"].as(REST_API_DEFAULT_PORT); - std::string restApiPassword = controlCh["restPassword"].as(); - - VoiceChData data = VoiceChData(m_channelId, m_channelNo, restApiAddress, restApiPort, restApiPassword); - m_controlChData = data; - - if (!m_controlChData.address().empty() && m_controlChData.port() > 0) { - ::LogInfoEx(LOG_HOST, "Control Channel REST API Address %s:%u", m_controlChData.address().c_str(), m_controlChData.port()); - } else { - ::LogInfoEx(LOG_HOST, "No Control Channel REST API Configured, CC notify disabled"); - } - } - - /* - ** Voice Channels - */ - yaml::Node& voiceChList = rfssConfig["voiceChNo"]; - - if (voiceChList.size() == 0U) { - ::LogError(LOG_HOST, "No voice channel list defined!"); - return false; - } - - for (size_t i = 0; i < voiceChList.size(); i++) { - yaml::Node& channel = voiceChList[i]; - - uint8_t chId = (uint8_t)channel["channelId"].as(255U); - - // special case default handling for if the channelId field is missing from the - // configuration - if (chId == 255U) { - chId = m_channelId; - } - - if (chId > 15U) { // clamp to 15 - chId = 15U; - } - - uint32_t chNo = (uint32_t)::strtoul(channel["channelNo"].as("1").c_str(), NULL, 16); - if (chNo == 0U) { // clamp to 1 - chNo = 1U; - } - if (chNo > 4095U) { // clamp to 4095 - chNo = 4095U; - } - - std::string restApiAddress = channel["restAddress"].as("127.0.0.1"); - uint16_t restApiPort = (uint16_t)channel["restPort"].as(REST_API_DEFAULT_PORT); - std::string restApiPassword = channel["restPassword"].as(); - - ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u", chId, chNo, restApiAddress.c_str(), restApiPort); - - VoiceChData data = VoiceChData(chId, chNo, restApiAddress, restApiPort, restApiPassword); - m_voiceChData[chNo] = data; - m_voiceChNo.push_back(chNo); - } - - std::string strVoiceChNo = ""; - for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { - uint32_t chNo = ::atoi(std::to_string(*it).c_str()); - ::lookups::VoiceChData voiceChData = m_voiceChData[chNo]; - - char hexStr[29]; - - ::sprintf(hexStr, "$%01X.%01X (%u.%u)", voiceChData.chId(), chNo, voiceChData.chId(), chNo); - - strVoiceChNo.append(std::string(hexStr)); - strVoiceChNo.append(","); - } - strVoiceChNo.erase(strVoiceChNo.find_last_of(",")); - - /* - ** Site Parameters - */ - m_siteId = (uint8_t)::strtoul(rfssConfig["siteId"].as("1").c_str(), NULL, 16); - m_siteId = p25::P25Utils::siteId(m_siteId); - - m_dmrColorCode = rfssConfig["colorCode"].as(2U); - m_dmrColorCode = dmr::DMRUtils::colorCode(m_dmrColorCode); - - m_dmrNetId = (uint32_t)::strtoul(rfssConfig["dmrNetId"].as("1").c_str(), NULL, 16); - m_dmrNetId = dmr::DMRUtils::netId(m_dmrNetId, dmr::SITE_MODEL_TINY); - - m_p25NAC = (uint32_t)::strtoul(rfssConfig["nac"].as("293").c_str(), NULL, 16); - m_p25NAC = p25::P25Utils::nac(m_p25NAC); - - uint32_t p25TxNAC = (uint32_t)::strtoul(rfssConfig["txNAC"].as("F7E").c_str(), NULL, 16); - if (p25TxNAC == m_p25NAC) { - LogWarning(LOG_HOST, "Only use txNAC when split NAC operations are needed. nac and txNAC should not be the same!"); - } - - m_p25NetId = (uint32_t)::strtoul(rfssConfig["netId"].as("BB800").c_str(), NULL, 16); - m_p25NetId = p25::P25Utils::netId(m_p25NetId); - if (m_p25NetId == 0xBEE00) { - ::fatal("error 4\n"); - } - - m_sysId = (uint32_t)::strtoul(rfssConfig["sysId"].as("001").c_str(), NULL, 16); - m_sysId = p25::P25Utils::sysId(m_sysId); - - m_p25RfssId = (uint8_t)::strtoul(rfssConfig["rfssId"].as("1").c_str(), NULL, 16); - m_p25RfssId = p25::P25Utils::rfssId(m_p25RfssId); - - m_nxdnRAN = rfssConfig["ran"].as(1U); - - m_authoritative = rfssConfig["authoritative"].as(true); - - LogInfo("System Config Parameters"); - LogInfo(" Authoritative: %s", m_authoritative ? "yes" : "no"); - if (m_authoritative) { - m_supervisor = rfssConfig["supervisor"].as(false); - LogInfo(" Supervisor: %s", m_supervisor ? "yes" : "no"); - } - LogInfo(" RX Frequency: %uHz", m_rxFrequency); - LogInfo(" TX Frequency: %uHz", m_txFrequency); - LogInfo(" Base Frequency: %uHz", entry.baseFrequency()); - LogInfo(" TX Offset: %fMHz", entry.txOffsetMhz()); - LogInfo(" Bandwidth: %fKHz", entry.chBandwidthKhz()); - LogInfo(" Channel Spacing: %fKHz", entry.chSpaceKhz()); - LogInfo(" Channel Id: %u", m_channelId); - LogInfo(" Channel No.: $%04X (%u)", m_channelNo, m_channelNo); - LogInfo(" Voice Channel No(s).: %s", strVoiceChNo.c_str()); - LogInfo(" Site Id: $%02X", m_siteId); - LogInfo(" System Id: $%03X", m_sysId); - LogInfo(" DMR Color Code: %u", m_dmrColorCode); - LogInfo(" DMR Network Id: $%05X", m_dmrNetId); - LogInfo(" P25 NAC: $%03X", m_p25NAC); - - if (p25TxNAC != 0xF7EU && p25TxNAC != m_p25NAC) { - LogInfo(" P25 Tx NAC: $%03X", p25TxNAC); - } - - LogInfo(" P25 Network Id: $%05X", m_p25NetId); - LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId); - LogInfo(" NXDN RAN: %u", m_nxdnRAN); - - if (!m_authoritative) { - m_supervisor = false; - LogWarning(LOG_HOST, "Host is non-authoritative! This requires REST API to handle permit TG for VCs and grant TG for CCs!"); - } - } - else { - LogInfo(" Modem Remote Control: yes"); - } - - return true; -} - -/// -/// Initializes the modem DSP. -/// -bool Host::createModem() -{ - yaml::Node protocolConf = m_conf["protocols"]; - - yaml::Node dmrProtocol = protocolConf["dmr"]; - uint32_t dmrQueueSize = dmrProtocol["queueSize"].as(24U); - - // clamp queue size to no less then 24 and no greater the 100 - if (dmrQueueSize < 24U) { - LogWarning(LOG_HOST, "DMR queue size must be greater then 24 frames, defaulting to 24 frames!"); - dmrQueueSize = 24U; - } - if (dmrQueueSize > 100U) { - LogWarning(LOG_HOST, "DMR queue size must be less then 100 frames, defaulting to 100 frames!"); - dmrQueueSize = 100U; - } - if (dmrQueueSize > 60U) { - LogWarning(LOG_HOST, "DMR queue size is excessive, >60 frames!"); - } - - m_dmrQueueSizeBytes = dmrQueueSize * (dmr::DMR_FRAME_LENGTH_BYTES * 5U); - - yaml::Node p25Protocol = protocolConf["p25"]; - uint32_t p25QueueSize = p25Protocol["queueSize"].as(12U); - - // clamp queue size to no less then 12 and no greater the 100 frames - if (p25QueueSize < 12U) { - LogWarning(LOG_HOST, "P25 queue size must be greater then 12 frames, defaulting to 12 frames!"); - p25QueueSize = 12U; - } - if (p25QueueSize > 50U) { - LogWarning(LOG_HOST, "P25 queue size must be less then 50 frames, defaulting to 50 frames!"); - p25QueueSize = 50U; - } - if (p25QueueSize > 30U) { - LogWarning(LOG_HOST, "P25 queue size is excessive, >30 frames!"); - } - - m_p25QueueSizeBytes = p25QueueSize * p25::P25_LDU_FRAME_LENGTH_BYTES; - - yaml::Node nxdnProtocol = protocolConf["nxdn"]; - uint32_t nxdnQueueSize = nxdnProtocol["queueSize"].as(31U); - - // clamp queue size to no less then 31 and no greater the 50 frames - if (nxdnQueueSize < 31U) { - LogWarning(LOG_HOST, "NXDN queue size must be greater then 31 frames, defaulting to 31 frames!"); - nxdnQueueSize = 31U; - } - if (nxdnQueueSize > 50U) { - LogWarning(LOG_HOST, "NXDN queue size must be less then 50 frames, defaulting to 50 frames!"); - nxdnQueueSize = 50U; - } - - m_nxdnQueueSizeBytes = nxdnQueueSize * nxdn::NXDN_FRAME_LENGTH_BYTES; - - yaml::Node modemConf = m_conf["system"]["modem"]; - - yaml::Node modemProtocol = modemConf["protocol"]; - std::string portType = modemProtocol["type"].as("null"); - yaml::Node uartProtocol = modemProtocol["uart"]; - std::string uartPort = uartProtocol["port"].as(); - uint32_t uartSpeed = uartProtocol["speed"].as(115200); - - bool rxInvert = modemConf["rxInvert"].as(false); - bool txInvert = modemConf["txInvert"].as(false); - bool pttInvert = modemConf["pttInvert"].as(false); - bool dcBlocker = modemConf["dcBlocker"].as(true); - bool cosLockout = modemConf["cosLockout"].as(false); - uint8_t fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U); - uint8_t dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); - uint8_t p25CorrCount = (uint8_t)modemConf["p25CorrCount"].as(4U); - int rxDCOffset = modemConf["rxDCOffset"].as(0); - int txDCOffset = modemConf["txDCOffset"].as(0); - - yaml::Node hotspotParams = modemConf["hotspot"]; - - int dmrDiscBWAdj = hotspotParams["dmrDiscBWAdj"].as(0); - int p25DiscBWAdj = hotspotParams["p25DiscBWAdj"].as(0); - int nxdnDiscBWAdj = hotspotParams["nxdnDiscBWAdj"].as(0); - int dmrPostBWAdj = hotspotParams["dmrPostBWAdj"].as(0); - int p25PostBWAdj = hotspotParams["p25PostBWAdj"].as(0); - int nxdnPostBWAdj = hotspotParams["nxdnPostBWAdj"].as(0); - ADF_GAIN_MODE adfGainMode = (ADF_GAIN_MODE)hotspotParams["adfGainMode"].as(0U); - bool afcEnable = hotspotParams["afcEnable"].as(false); - uint8_t afcKI = (uint8_t)hotspotParams["afcKI"].as(11U); - uint8_t afcKP = (uint8_t)hotspotParams["afcKP"].as(4U); - uint8_t afcRange = (uint8_t)hotspotParams["afcRange"].as(1U); - int rxTuning = hotspotParams["rxTuning"].as(0); - int txTuning = hotspotParams["txTuning"].as(0); - uint8_t rfPower = (uint8_t)hotspotParams["rfPower"].as(100U); - - yaml::Node repeaterParams = modemConf["repeater"]; - - int dmrSymLevel3Adj = repeaterParams["dmrSymLvl3Adj"].as(0); - int dmrSymLevel1Adj = repeaterParams["dmrSymLvl1Adj"].as(0); - int p25SymLevel3Adj = repeaterParams["p25SymLvl3Adj"].as(0); - int p25SymLevel1Adj = repeaterParams["p25SymLvl1Adj"].as(0); - int nxdnSymLevel3Adj = repeaterParams["nxdnSymLvl3Adj"].as(0); - int nxdnSymLevel1Adj = repeaterParams["nxdnSymLvl1Adj"].as(0); - - yaml::Node softpotParams = modemConf["softpot"]; - - uint8_t rxCoarse = (uint8_t)softpotParams["rxCoarse"].as(127U); - uint8_t rxFine = (uint8_t)softpotParams["rxFine"].as(127U); - uint8_t txCoarse = (uint8_t)softpotParams["txCoarse"].as(127U); - uint8_t txFine = (uint8_t)softpotParams["txFine"].as(127U); - uint8_t rssiCoarse = (uint8_t)softpotParams["rssiCoarse"].as(127U); - uint8_t rssiFine = (uint8_t)softpotParams["rssiFine"].as(127U); - - uint16_t dmrFifoLength = (uint16_t)modemConf["dmrFifoLength"].as(DMR_TX_BUFFER_LEN); - uint16_t p25FifoLength = (uint16_t)modemConf["p25FifoLength"].as(P25_TX_BUFFER_LEN); - uint16_t nxdnFifoLength = (uint16_t)modemConf["nxdnFifoLength"].as(NXDN_TX_BUFFER_LEN); - - float rxLevel = modemConf["rxLevel"].as(50.0F); - float cwIdTXLevel = modemConf["cwIdTxLevel"].as(50.0F); - float dmrTXLevel = modemConf["dmrTxLevel"].as(50.0F); - float p25TXLevel = modemConf["p25TxLevel"].as(50.0F); - float nxdnTXLevel = modemConf["nxdnTxLevel"].as(50.0F); - if (!modemConf["txLevel"].isNone()) { - cwIdTXLevel = dmrTXLevel = p25TXLevel = nxdnTXLevel = modemConf["txLevel"].as(50.0F); - } - bool disableOFlowReset = modemConf["disableOFlowReset"].as(false); - bool ignoreModemConfigArea = modemConf["ignoreModemConfigArea"].as(false); - bool dumpModemStatus = modemConf["dumpModemStatus"].as(false); - bool trace = modemConf["trace"].as(false); - bool debug = modemConf["debug"].as(false); - - if (rfPower == 0U) { // clamp to 1 - rfPower = 1U; - } - if (rfPower > 100U) { // clamp to 100 - rfPower = 100U; - } - - LogInfo("Modem Parameters"); - LogInfo(" Port Type: %s", portType.c_str()); - - port::IModemPort* modemPort = nullptr; - std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); - if (portType == NULL_PORT) { - modemPort = new port::ModemNullPort(); - } - else if (portType == UART_PORT || portType == PTY_PORT) { - port::SERIAL_SPEED serialSpeed = port::SERIAL_115200; - switch (uartSpeed) { - case 1200: - serialSpeed = port::SERIAL_1200; - break; - case 2400: - serialSpeed = port::SERIAL_2400; - break; - case 4800: - serialSpeed = port::SERIAL_4800; - break; - case 9600: - serialSpeed = port::SERIAL_9600; - break; - case 19200: - serialSpeed = port::SERIAL_19200; - break; - case 38400: - serialSpeed = port::SERIAL_38400; - break; - case 76800: - serialSpeed = port::SERIAL_76800; - break; - case 230400: - serialSpeed = port::SERIAL_230400; - break; - case 460800: - serialSpeed = port::SERIAL_460800; - break; - default: - LogWarning(LOG_HOST, "Unsupported serial speed %u, defaulting to %u", uartSpeed, port::SERIAL_115200); - uartSpeed = 115200; - case 115200: - break; - } - - if (portType == PTY_PORT) { - modemPort = new port::UARTPort(uartPort, serialSpeed, false); - LogInfo(" PTY Port: %s", uartPort.c_str()); - LogInfo(" PTY Speed: %u", uartSpeed); - } - else { - modemPort = new port::UARTPort(uartPort, serialSpeed, true); - LogInfo(" UART Port: %s", uartPort.c_str()); - LogInfo(" UART Speed: %u", uartSpeed); - } - } - else { - LogError(LOG_HOST, "Invalid protocol port type, %s!", portType.c_str()); - return false; - } - - if (g_remoteModemMode) { - if (portType == UART_PORT || portType == PTY_PORT) { - m_modemRemotePort = new port::UDPPort(g_remoteAddress, g_remotePort); - m_modemRemote = true; - ignoreModemConfigArea = true; - - } - else { - delete modemPort; - modemPort = new port::UDPPort(g_remoteAddress, g_remotePort); - m_modemRemote = false; - } - - LogInfo(" UDP Mode: %s", m_modemRemote ? "master" : "peer"); - LogInfo(" UDP Address: %s", g_remoteAddress.c_str()); - LogInfo(" UDP Port: %u", g_remotePort); - } - - if (!m_modemRemote) { - LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); - LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); - LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); - LogInfo(" DC Blocker: %s", dcBlocker ? "yes" : "no"); - LogInfo(" COS Lockout: %s", cosLockout ? "yes" : "no"); - LogInfo(" FDMA Preambles: %u (%.1fms)", fdmaPreamble, float(fdmaPreamble) * 0.2222F); - LogInfo(" DMR RX Delay: %u (%.1fms)", dmrRxDelay, float(dmrRxDelay) * 0.0416666F); - LogInfo(" P25 Corr. Count: %u (%.1fms)", p25CorrCount, float(p25CorrCount) * 0.667F); - LogInfo(" RX DC Offset: %d", rxDCOffset); - LogInfo(" TX DC Offset: %d", txDCOffset); - LogInfo(" RX Tuning Offset: %dhz", rxTuning); - LogInfo(" TX Tuning Offset: %dhz", txTuning); - LogInfo(" RX Effective Frequency: %uhz", m_rxFrequency + rxTuning); - LogInfo(" TX Effective Frequency: %uhz", m_txFrequency + txTuning); - LogInfo(" RX Coarse: %u, Fine: %u", rxCoarse, rxFine); - LogInfo(" TX Coarse: %u, Fine: %u", txCoarse, txFine); - LogInfo(" RSSI Coarse: %u, Fine: %u", rssiCoarse, rssiFine); - LogInfo(" RF Power Level: %u", rfPower); - LogInfo(" RX Level: %.1f%%", rxLevel); - LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); - LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); - LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); - LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); - LogInfo(" Disable Overflow Reset: %s", disableOFlowReset ? "yes" : "no"); - LogInfo(" DMR Queue Size: %u (%u bytes)", dmrQueueSize, m_dmrQueueSizeBytes); - LogInfo(" P25 Queue Size: %u (%u bytes)", p25QueueSize, m_p25QueueSizeBytes); - LogInfo(" NXDN Queue Size: %u (%u bytes)", nxdnQueueSize, m_nxdnQueueSizeBytes); - LogInfo(" DMR FIFO Size: %u bytes", dmrFifoLength); - LogInfo(" P25 FIFO Size: %u bytes", p25FifoLength); - LogInfo(" NXDN FIFO Size: %u bytes", nxdnFifoLength); - - if (ignoreModemConfigArea) { - LogInfo(" Ignore Modem Configuration Area: yes"); - } - - if (dumpModemStatus) { - LogInfo(" Dump Modem Status: yes"); - } - } - - if (debug) { - LogInfo(" Debug: yes"); - } - - m_modem = new Modem(modemPort, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, - m_dmrQueueSizeBytes, m_p25QueueSizeBytes, m_nxdnQueueSizeBytes, disableOFlowReset, ignoreModemConfigArea, dumpModemStatus, trace, debug); - if (!m_modemRemote) { - m_modem->setModeParams(m_dmrEnabled, m_p25Enabled, m_nxdnEnabled); - m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel, nxdnTXLevel); - m_modem->setSymbolAdjust(dmrSymLevel3Adj, dmrSymLevel1Adj, p25SymLevel3Adj, p25SymLevel1Adj, nxdnSymLevel3Adj, nxdnSymLevel1Adj); - m_modem->setDCOffsetParams(txDCOffset, rxDCOffset); - m_modem->setRFParams(m_rxFrequency, m_txFrequency, rxTuning, txTuning, rfPower, dmrDiscBWAdj, p25DiscBWAdj, nxdnDiscBWAdj, dmrPostBWAdj, - p25PostBWAdj, nxdnPostBWAdj, adfGainMode, afcEnable, afcKI, afcKP, afcRange); - m_modem->setSoftPot(rxCoarse, rxFine, txCoarse, txFine, rssiCoarse, rssiFine); - m_modem->setDMRColorCode(m_dmrColorCode); - m_modem->setP25NAC(m_p25NAC); - } - - if (m_modemRemote) { - m_modem->setOpenHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemOpen, this)); - m_modem->setCloseHandler(MODEM_OC_PORT_HANDLER_BIND(Host::rmtPortModemClose, this)); - m_modem->setResponseHandler(MODEM_RESP_HANDLER_BIND(Host::rmtPortModemHandler, this)); - } - - bool ret = m_modem->open(); - if (!ret) { - delete m_modem; - m_modem = nullptr; - return false; - } - - m_modem->setFifoLength(dmrFifoLength, p25FifoLength, nxdnFifoLength); - - // are we on a protocol version older then 3? - if (m_modem->getVersion() < 3U) { - if (m_nxdnEnabled) { - ::LogError(LOG_HOST, "NXDN is not supported on legacy firmware."); - return false; - } - } - - return true; -} - -/// -/// Initializes network connectivity. -/// -bool Host::createNetwork() -{ - yaml::Node networkConf = m_conf["network"]; - bool netEnable = networkConf["enable"].as(false); - bool restApiEnable = networkConf["restEnable"].as(false); - - // dump out if both networking and REST API are disabled - if (!netEnable && !restApiEnable) { - return true; - } - - std::string address = networkConf["address"].as(); - uint16_t port = (uint16_t)networkConf["port"].as(TRAFFIC_DEFAULT_PORT); - uint16_t local = (uint16_t)networkConf["local"].as(0U); - std::string restApiAddress = networkConf["restAddress"].as("127.0.0.1"); - uint16_t restApiPort = (uint16_t)networkConf["restPort"].as(REST_API_DEFAULT_PORT); - std::string restApiPassword = networkConf["restPassword"].as(); - bool restApiDebug = networkConf["restDebug"].as(false); - uint32_t id = networkConf["id"].as(1000U); - uint32_t jitter = networkConf["talkgroupHang"].as(360U); - std::string password = networkConf["password"].as(); - bool slot1 = networkConf["slot1"].as(true); - bool slot2 = networkConf["slot2"].as(true); - bool allowActivityTransfer = networkConf["allowActivityTransfer"].as(false); - bool allowDiagnosticTransfer = networkConf["allowDiagnosticTransfer"].as(false); - bool updateLookup = networkConf["updateLookups"].as(false); - bool debug = networkConf["debug"].as(false); - - if (id > 999999999U) { - ::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999."); - return false; - } - - if (restApiPassword.length() > 64) { - std::string password = restApiPassword; - restApiPassword = password.substr(0, 64); - - ::LogWarning(LOG_HOST, "REST API password is too long; truncating to the first 64 characters."); - } - - if (restApiPassword.empty() && restApiEnable) { - ::LogWarning(LOG_HOST, "REST API password not provided; REST API disabled."); - restApiEnable = false; - } - - IdenTable entry = m_idenTable->find(m_channelId); - - LogInfo("Network Parameters"); - LogInfo(" Enabled: %s", netEnable ? "yes" : "no"); - if (netEnable) { - LogInfo(" Peer ID: %u", id); - LogInfo(" Address: %s", address.c_str()); - LogInfo(" Port: %u", port); - if (local > 0U) - LogInfo(" Local: %u", local); - else - LogInfo(" Local: random"); - LogInfo(" DMR Jitter: %ums", jitter); - LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); - LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); - LogInfo(" Allow Activity Log Transfer: %s", allowActivityTransfer ? "yes" : "no"); - LogInfo(" Allow Diagnostic Log Transfer: %s", allowDiagnosticTransfer ? "yes" : "no"); - LogInfo(" Update Lookups: %s", updateLookup ? "yes" : "no"); - - if (debug) { - LogInfo(" Debug: yes"); - } - } - LogInfo(" REST API Enabled: %s", restApiEnable ? "yes" : "no"); - if (restApiEnable) { - LogInfo(" REST API Address: %s", restApiAddress.c_str()); - LogInfo(" REST API Port: %u", restApiPort); - - if (restApiDebug) { - LogInfo(" REST API Debug: yes"); - } - } - - // initialize networking - if (netEnable) { - m_network = new Network(address, port, local, id, password, m_duplex, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup); - - m_network->setLookups(m_ridLookup, m_tidLookup); - m_network->setMetadata(m_identity, m_rxFrequency, m_txFrequency, entry.txOffsetMhz(), entry.chBandwidthKhz(), m_channelId, m_channelNo, - m_power, m_latitude, m_longitude, m_height, m_location); - if (restApiEnable) { - m_network->setRESTAPIData(restApiPassword, restApiPort); - } - - m_network->enable(true); - bool ret = m_network->open(); - if (!ret) { - delete m_network; - m_network = nullptr; - LogError(LOG_HOST, "failed to initialize traffic networking!"); - return false; - } - - ::LogSetNetwork(m_network); - } - - // initialize network remote command - if (restApiEnable) { - m_RESTAPI = new RESTAPI(restApiAddress, restApiPort, restApiPassword, this, restApiDebug); - m_RESTAPI->setLookups(m_ridLookup, m_tidLookup); - bool ret = m_RESTAPI->open(); - if (!ret) { - delete m_RESTAPI; - m_RESTAPI = nullptr; - LogError(LOG_HOST, "failed to initialize REST API networking! REST API will be unavailable!"); - // REST API failing isn't fatal -- we'll allow this to return normally - } - } - else { - m_RESTAPI = nullptr; - } - - return true; -} - /// /// /// diff --git a/src/host/Host.h b/src/host/Host.h index 07423a3b..aa12a1b4 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -165,13 +165,6 @@ private: friend class RESTAPI; RESTAPI* m_RESTAPI; - /// Reads basic configuration parameters from the INI. - bool readParams(); - /// Initializes the modem DSP. - bool createModem(); - /// Initializes network connectivity. - bool createNetwork(); - /// Modem port open callback. bool rmtPortModemOpen(modem::Modem* modem); /// Modem port close callback. @@ -187,7 +180,15 @@ private: /// Helper to remove the state lock file. void removeLockFile() const; - /** Digital Mobile Radio */ + /** (Host.Config.cpp) */ + /// Reads basic configuration parameters from the INI. + bool readParams(); + /// Initializes the modem DSP. + bool createModem(); + /// Initializes network connectivity. + bool createNetwork(); + + /** Digital Mobile Radio (Host.DMR.cpp) */ /// Helper to interrupt a running DMR beacon. void interruptDMRBeacon(dmr::Control* control); @@ -200,7 +201,7 @@ private: /// Helper to write DMR slot 2 frames to modem. void writeFramesDMR2(dmr::Control* control, std::function&& afterWriteCallback); - /** Project 25 */ + /** Project 25 (Host.P25.cpp) */ /// Helper to interrupt a running P25 control channel. void interruptP25Control(p25::Control* control); @@ -209,7 +210,7 @@ private: /// Helper to write P25 frames to modem. void writeFramesP25(p25::Control* control, std::function&& afterWriteCallback); - /** Next Generation Digital Narrowband */ + /** Next Generation Digital Narrowband (Host.NXDN.cpp) */ /// Helper to interrupt a running NXDN control channel. void interruptNXDNControl(nxdn::Control* control);