diff --git a/AX25Network.cpp b/AX25Network.cpp index 2f25a47..192b6df 100644 --- a/AX25Network.cpp +++ b/AX25Network.cpp @@ -30,7 +30,7 @@ const unsigned int BUFFER_LENGTH = 500U; CAX25Network::CAX25Network(const std::string& port, unsigned int speed, bool debug) : -m_serial(port, SERIAL_SPEED(speed), false), // XXX +m_serial(port, speed, false), m_txData(NULL), m_rxData(NULL), m_rxLength(0U), diff --git a/Conf.cpp b/Conf.cpp index 0ef321f..fc6dad7 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -28,39 +28,40 @@ const int BUFFER_SIZE = 500; enum SECTION { - SECTION_NONE, - SECTION_GENERAL, - SECTION_INFO, - SECTION_LOG, - SECTION_CWID, - SECTION_DMRID_LOOKUP, - SECTION_NXDNID_LOOKUP, - SECTION_MODEM, - SECTION_TRANSPARENT, - SECTION_UMP, - SECTION_DSTAR, - SECTION_DMR, - SECTION_FUSION, - SECTION_P25, - SECTION_NXDN, - SECTION_POCSAG, - SECTION_FM, - SECTION_AX25, - SECTION_DSTAR_NETWORK, - SECTION_DMR_NETWORK, - SECTION_FUSION_NETWORK, - SECTION_P25_NETWORK, - SECTION_NXDN_NETWORK, - SECTION_POCSAG_NETWORK, - SECTION_AX25_NETWORK, - SECTION_TFTSERIAL, - SECTION_HD44780, - SECTION_NEXTION, - SECTION_OLED, - SECTION_LCDPROC, - SECTION_LOCK_FILE, - SECTION_MOBILE_GPS, - SECTION_REMOTE_CONTROL + SECTION_NONE, + SECTION_GENERAL, + SECTION_INFO, + SECTION_LOG, + SECTION_CWID, + SECTION_DMRID_LOOKUP, + SECTION_NXDNID_LOOKUP, + SECTION_MODEM, + SECTION_TRANSPARENT, + SECTION_UMP, + SECTION_DSTAR, + SECTION_DMR, + SECTION_FUSION, + SECTION_P25, + SECTION_NXDN, + SECTION_POCSAG, + SECTION_FM, + SECTION_AX25, + SECTION_DSTAR_NETWORK, + SECTION_DMR_NETWORK, + SECTION_FUSION_NETWORK, + SECTION_P25_NETWORK, + SECTION_NXDN_NETWORK, + SECTION_POCSAG_NETWORK, + SECTION_FM_NETWORK, + SECTION_AX25_NETWORK, + SECTION_TFTSERIAL, + SECTION_HD44780, + SECTION_NEXTION, + SECTION_OLED, + SECTION_LCDPROC, + SECTION_LOCK_FILE, + SECTION_MOBILE_GPS, + SECTION_REMOTE_CONTROL }; CConf::CConf(const std::string& file) : @@ -93,6 +94,7 @@ m_nxdnIdLookupFile(), m_nxdnIdLookupTime(0U), m_modemPort(), m_modemProtocol("uart"), +m_modemSpeed(115200U), m_modemAddress(0x22), m_modemRXInvert(false), m_modemTXInvert(false), @@ -202,12 +204,14 @@ m_fmCTCSSHighThreshold(30U), m_fmCTCSSLowThreshold(20U), m_fmCTCSSLevel(2.0F), m_fmKerchunkTime(0U), +m_fmKerchunkTX(true), m_fmHangTime(7U), m_fmUseCOS(true), m_fmCOSInvert(false), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtAudioBoost(1U), +m_fmModeHang(10U), m_ax25Enabled(false), m_ax25RXTwist(6), m_ax25TXTwist(6), @@ -257,6 +261,13 @@ m_pocsagLocalAddress(), m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), +m_fmNetworkEnabled(false), +m_fmGatewayAddress(), +m_fmGatewayPort(0U), +m_fmLocalAddress(), +m_fmLocalPort(0U), +m_fmNetworkModeHang(3U), +m_fmNetworkDebug(false), m_ax25NetworkEnabled(false), m_ax25NetworkPort(), m_ax25NetworkSpeed(9600U), @@ -308,350 +319,354 @@ CConf::~CConf() bool CConf::read() { - FILE* fp = ::fopen(m_file.c_str(), "rt"); - if (fp == NULL) { - ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); - return false; - } + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } - SECTION section = SECTION_NONE; + SECTION section = SECTION_NONE; - char buffer[BUFFER_SIZE]; - while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { - if (buffer[0U] == '#') - continue; + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; - if (buffer[0U] == '[') { - if (::strncmp(buffer, "[General]", 9U) == 0) - section = SECTION_GENERAL; - else if (::strncmp(buffer, "[Info]", 6U) == 0) - section = SECTION_INFO; - else if (::strncmp(buffer, "[Log]", 5U) == 0) - section = SECTION_LOG; - else if (::strncmp(buffer, "[CW Id]", 7U) == 0) - section = SECTION_CWID; - else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0) - section = SECTION_DMRID_LOOKUP; - else if (::strncmp(buffer, "[NXDN Id Lookup]", 16U) == 0) - section = SECTION_NXDNID_LOOKUP; - else if (::strncmp(buffer, "[Modem]", 7U) == 0) - section = SECTION_MODEM; - else if (::strncmp(buffer, "[Transparent Data]", 18U) == 0) - section = SECTION_TRANSPARENT; - else if (::strncmp(buffer, "[UMP]", 5U) == 0) - section = SECTION_UMP; - else if (::strncmp(buffer, "[D-Star]", 8U) == 0) - section = SECTION_DSTAR; - else if (::strncmp(buffer, "[DMR]", 5U) == 0) - section = SECTION_DMR; - else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) - section = SECTION_FUSION; - else if (::strncmp(buffer, "[P25]", 5U) == 0) - section = SECTION_P25; - else if (::strncmp(buffer, "[NXDN]", 6U) == 0) - section = SECTION_NXDN; - else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) - section = SECTION_POCSAG; - else if (::strncmp(buffer, "[FM]", 4U) == 0) - section = SECTION_FM; - else if (::strncmp(buffer, "[AX.25]", 7U) == 0) - section = SECTION_AX25; - else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) - section = SECTION_DSTAR_NETWORK; - else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) - section = SECTION_DMR_NETWORK; - else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) - section = SECTION_FUSION_NETWORK; - else if (::strncmp(buffer, "[P25 Network]", 13U) == 0) - section = SECTION_P25_NETWORK; - else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) - section = SECTION_NXDN_NETWORK; - else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) - section = SECTION_POCSAG_NETWORK; - else if (::strncmp(buffer, "[AX.25 Network]", 15U) == 0) - section = SECTION_AX25_NETWORK; - else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) - section = SECTION_TFTSERIAL; - else if (::strncmp(buffer, "[HD44780]", 9U) == 0) - section = SECTION_HD44780; - else if (::strncmp(buffer, "[Nextion]", 9U) == 0) - section = SECTION_NEXTION; - else if (::strncmp(buffer, "[OLED]", 6U) == 0) - section = SECTION_OLED; - else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) - section = SECTION_LCDPROC; - else if (::strncmp(buffer, "[Lock File]", 11U) == 0) - section = SECTION_LOCK_FILE; - else if (::strncmp(buffer, "[Mobile GPS]", 12U) == 0) - section = SECTION_MOBILE_GPS; - else if (::strncmp(buffer, "[Remote Control]", 16U) == 0) - section = SECTION_REMOTE_CONTROL; - else - section = SECTION_NONE; + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Info]", 6U) == 0) + section = SECTION_INFO; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[CW Id]", 7U) == 0) + section = SECTION_CWID; + else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0) + section = SECTION_DMRID_LOOKUP; + else if (::strncmp(buffer, "[NXDN Id Lookup]", 16U) == 0) + section = SECTION_NXDNID_LOOKUP; + else if (::strncmp(buffer, "[Modem]", 7U) == 0) + section = SECTION_MODEM; + else if (::strncmp(buffer, "[Transparent Data]", 18U) == 0) + section = SECTION_TRANSPARENT; + else if (::strncmp(buffer, "[UMP]", 5U) == 0) + section = SECTION_UMP; + else if (::strncmp(buffer, "[D-Star]", 8U) == 0) + section = SECTION_DSTAR; + else if (::strncmp(buffer, "[DMR]", 5U) == 0) + section = SECTION_DMR; + else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) + section = SECTION_FUSION; + else if (::strncmp(buffer, "[P25]", 5U) == 0) + section = SECTION_P25; + else if (::strncmp(buffer, "[NXDN]", 6U) == 0) + section = SECTION_NXDN; + else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) + section = SECTION_POCSAG; + else if (::strncmp(buffer, "[FM]", 4U) == 0) + section = SECTION_FM; + else if (::strncmp(buffer, "[AX.25]", 7U) == 0) + section = SECTION_AX25; + else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) + section = SECTION_DSTAR_NETWORK; + else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) + section = SECTION_DMR_NETWORK; + else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) + section = SECTION_FUSION_NETWORK; + else if (::strncmp(buffer, "[P25 Network]", 13U) == 0) + section = SECTION_P25_NETWORK; + else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) + section = SECTION_NXDN_NETWORK; + else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) + section = SECTION_POCSAG_NETWORK; + else if (::strncmp(buffer, "[FM Network]", 12U) == 0) + section = SECTION_FM_NETWORK; + else if (::strncmp(buffer, "[AX.25 Network]", 15U) == 0) + section = SECTION_AX25_NETWORK; + else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) + section = SECTION_TFTSERIAL; + else if (::strncmp(buffer, "[HD44780]", 9U) == 0) + section = SECTION_HD44780; + else if (::strncmp(buffer, "[Nextion]", 9U) == 0) + section = SECTION_NEXTION; + else if (::strncmp(buffer, "[OLED]", 6U) == 0) + section = SECTION_OLED; + else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) + section = SECTION_LCDPROC; + else if (::strncmp(buffer, "[Lock File]", 11U) == 0) + section = SECTION_LOCK_FILE; + else if (::strncmp(buffer, "[Mobile GPS]", 12U) == 0) + section = SECTION_MOBILE_GPS; + else if (::strncmp(buffer, "[Remote Control]", 16U) == 0) + section = SECTION_REMOTE_CONTROL; + else + section = SECTION_NONE; - continue; - } - - char* key = ::strtok(buffer, " \t=\r\n"); - if (key == NULL) - continue; - - char* value = ::strtok(NULL, "\r\n"); - if (value == NULL) - continue; - - // Remove quotes from the value - size_t len = ::strlen(value); - if (len > 1U && *value == '"' && value[len - 1U] == '"') { - value[len - 1U] = '\0'; - value++; - } - - if (section == SECTION_GENERAL) { - if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmCallsign = m_cwIdCallsign = m_callsign = value; - } else if (::strcmp(key, "Id") == 0) - m_id = m_p25Id = m_dmrId = (unsigned int)::atoi(value); - else if (::strcmp(key, "Timeout") == 0) - m_fmTimeout = m_timeout = (unsigned int)::atoi(value); - else if (::strcmp(key, "Duplex") == 0) - m_duplex = ::atoi(value) == 1; - else if (::strcmp(key, "ModeHang") == 0) - m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = - m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "RFModeHang") == 0) - m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "NetModeHang") == 0) - m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Display") == 0) - m_display = value; - else if (::strcmp(key, "Daemon") == 0) - m_daemon = ::atoi(value) == 1; - } else if (section == SECTION_INFO) { - if (::strcmp(key, "TXFrequency") == 0) - m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "RXFrequency") == 0) - m_rxFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "Power") == 0) - m_power = (unsigned int)::atoi(value); - else if (::strcmp(key, "Latitude") == 0) - m_latitude = float(::atof(value)); - else if (::strcmp(key, "Longitude") == 0) - m_longitude = float(::atof(value)); - else if (::strcmp(key, "Height") == 0) - m_height = ::atoi(value); - else if (::strcmp(key, "Location") == 0) - m_location = value; - else if (::strcmp(key, "Description") == 0) - m_description = value; - else if (::strcmp(key, "URL") == 0) - m_url = value; - } else if (section == SECTION_LOG) { - if (::strcmp(key, "FilePath") == 0) - m_logFilePath = value; - else if (::strcmp(key, "FileRoot") == 0) - m_logFileRoot = value; - else if (::strcmp(key, "FileLevel") == 0) - m_logFileLevel = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayLevel") == 0) - m_logDisplayLevel = (unsigned int)::atoi(value); - } else if (section == SECTION_CWID) { - if (::strcmp(key, "Enable") == 0) - m_cwIdEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Time") == 0) - m_cwIdTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_cwIdCallsign = value; + continue; } - } else if (section == SECTION_DMRID_LOOKUP) { - if (::strcmp(key, "File") == 0) - m_dmrIdLookupFile = value; - else if (::strcmp(key, "Time") == 0) - m_dmrIdLookupTime = (unsigned int)::atoi(value); - } else if (section == SECTION_NXDNID_LOOKUP) { - if (::strcmp(key, "File") == 0) - m_nxdnIdLookupFile = value; - else if (::strcmp(key, "Time") == 0) - m_nxdnIdLookupTime = (unsigned int)::atoi(value); - } else if (section == SECTION_MODEM) { - if (::strcmp(key, "Port") == 0) - m_modemPort = value; - else if (::strcmp(key, "Protocol") == 0) - m_modemProtocol = value; - else if (::strcmp(key, "Address") == 0) - m_modemAddress = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "RXInvert") == 0) - m_modemRXInvert = ::atoi(value) == 1; - else if (::strcmp(key, "TXInvert") == 0) - m_modemTXInvert = ::atoi(value) == 1; - else if (::strcmp(key, "PTTInvert") == 0) - m_modemPTTInvert = ::atoi(value) == 1; - else if (::strcmp(key, "TXDelay") == 0) - m_modemTXDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "DMRDelay") == 0) - m_modemDMRDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "RXOffset") == 0) - m_modemRXOffset = ::atoi(value); - else if (::strcmp(key, "TXOffset") == 0) - m_modemTXOffset = ::atoi(value); - else if (::strcmp(key, "RXDCOffset") == 0) - m_modemRXDCOffset = ::atoi(value); - else if (::strcmp(key, "TXDCOffset") == 0) - m_modemTXDCOffset = ::atoi(value); - else if (::strcmp(key, "RFLevel") == 0) - m_modemRFLevel = float(::atof(value)); - else if (::strcmp(key, "RXLevel") == 0) - m_modemRXLevel = float(::atof(value)); - else if (::strcmp(key, "TXLevel") == 0) - m_modemAX25TXLevel = m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = float(::atof(value)); - else if (::strcmp(key, "CWIdTXLevel") == 0) - m_modemCWIdTXLevel = float(::atof(value)); - else if (::strcmp(key, "D-StarTXLevel") == 0) - m_modemDStarTXLevel = float(::atof(value)); - else if (::strcmp(key, "DMRTXLevel") == 0) - m_modemDMRTXLevel = float(::atof(value)); - else if (::strcmp(key, "YSFTXLevel") == 0) - m_modemYSFTXLevel = float(::atof(value)); - else if (::strcmp(key, "P25TXLevel") == 0) - m_modemP25TXLevel = float(::atof(value)); - else if (::strcmp(key, "NXDNTXLevel") == 0) - m_modemNXDNTXLevel = float(::atof(value)); - else if (::strcmp(key, "POCSAGTXLevel") == 0) - m_modemPOCSAGTXLevel = float(::atof(value)); - else if (::strcmp(key, "FMTXLevel") == 0) - m_modemFMTXLevel = float(::atof(value)); - else if (::strcmp(key, "AX25TXLevel") == 0) - m_modemAX25TXLevel = float(::atof(value)); - else if (::strcmp(key, "RSSIMappingFile") == 0) - m_modemRSSIMappingFile = value; - else if (::strcmp(key, "Trace") == 0) - m_modemTrace = ::atoi(value) == 1; - else if (::strcmp(key, "Debug") == 0) - m_modemDebug = ::atoi(value) == 1; - } else if (section == SECTION_TRANSPARENT) { - if (::strcmp(key, "Enable") == 0) - m_transparentEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteAddress") == 0) - m_transparentRemoteAddress = value; - else if (::strcmp(key, "RemotePort") == 0) - m_transparentRemotePort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_transparentLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "SendFrameType") == 0) - m_transparentSendFrameType = (unsigned int)::atoi(value); - } else if (section == SECTION_UMP) { - if (::strcmp(key, "Enable") == 0) - m_umpEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Port") == 0) - m_umpPort = value; - } else if (section == SECTION_DSTAR) { - if (::strcmp(key, "Enable") == 0) - m_dstarEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Module") == 0) { - // Convert the module to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_dstarModule = value; - } else if (::strcmp(key, "SelfOnly") == 0) - m_dstarSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "BlackList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - if (::strlen(p) > 0U) { - for (unsigned int i = 0U; p[i] != 0; i++) - p[i] = ::toupper(p[i]); - std::string callsign = std::string(p); - callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); - m_dstarBlackList.push_back(callsign); + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (value == NULL) + continue; + + // Remove quotes from the value + size_t len = ::strlen(value); + if (len > 1U && *value == '"' && value[len - 1U] == '"') { + value[len - 1U] = '\0'; + value++; + } + + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmCallsign = m_cwIdCallsign = m_callsign = value; + } else if (::strcmp(key, "Id") == 0) + m_id = m_p25Id = m_dmrId = (unsigned int)::atoi(value); + else if (::strcmp(key, "Timeout") == 0) + m_fmTimeout = m_timeout = (unsigned int)::atoi(value); + else if (::strcmp(key, "Duplex") == 0) + m_duplex = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_pocsagNetworkModeHang = m_fmNetworkModeHang = + m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_fmModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "RFModeHang") == 0) + m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_fmModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "NetModeHang") == 0) + m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_pocsagNetworkModeHang = m_fmNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Display") == 0) + m_display = value; + else if (::strcmp(key, "Daemon") == 0) + m_daemon = ::atoi(value) == 1; + } else if (section == SECTION_INFO) { + if (::strcmp(key, "TXFrequency") == 0) + m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXFrequency") == 0) + m_rxFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "Power") == 0) + m_power = (unsigned int)::atoi(value); + else if (::strcmp(key, "Latitude") == 0) + m_latitude = float(::atof(value)); + else if (::strcmp(key, "Longitude") == 0) + m_longitude = float(::atof(value)); + else if (::strcmp(key, "Height") == 0) + m_height = ::atoi(value); + else if (::strcmp(key, "Location") == 0) + m_location = value; + else if (::strcmp(key, "Description") == 0) + m_description = value; + else if (::strcmp(key, "URL") == 0) + m_url = value; + } else if (section == SECTION_LOG) { + if (::strcmp(key, "FilePath") == 0) + m_logFilePath = value; + else if (::strcmp(key, "FileRoot") == 0) + m_logFileRoot = value; + else if (::strcmp(key, "FileLevel") == 0) + m_logFileLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayLevel") == 0) + m_logDisplayLevel = (unsigned int)::atoi(value); + } else if (section == SECTION_CWID) { + if (::strcmp(key, "Enable") == 0) + m_cwIdEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Time") == 0) + m_cwIdTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_cwIdCallsign = value; + } + } else if (section == SECTION_DMRID_LOOKUP) { + if (::strcmp(key, "File") == 0) + m_dmrIdLookupFile = value; + else if (::strcmp(key, "Time") == 0) + m_dmrIdLookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_NXDNID_LOOKUP) { + if (::strcmp(key, "File") == 0) + m_nxdnIdLookupFile = value; + else if (::strcmp(key, "Time") == 0) + m_nxdnIdLookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_MODEM) { + if (::strcmp(key, "Port") == 0) + m_modemPort = value; + else if (::strcmp(key, "Protocol") == 0) + m_modemProtocol = value; + else if (::strcmp(key, "Speed") == 0) + m_modemSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "Address") == 0) + m_modemAddress = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "RXInvert") == 0) + m_modemRXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXInvert") == 0) + m_modemTXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "PTTInvert") == 0) + m_modemPTTInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXDelay") == 0) + m_modemTXDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "DMRDelay") == 0) + m_modemDMRDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXOffset") == 0) + m_modemRXOffset = ::atoi(value); + else if (::strcmp(key, "TXOffset") == 0) + m_modemTXOffset = ::atoi(value); + else if (::strcmp(key, "RXDCOffset") == 0) + m_modemRXDCOffset = ::atoi(value); + else if (::strcmp(key, "TXDCOffset") == 0) + m_modemTXDCOffset = ::atoi(value); + else if (::strcmp(key, "RFLevel") == 0) + m_modemRFLevel = float(::atof(value)); + else if (::strcmp(key, "RXLevel") == 0) + m_modemRXLevel = float(::atof(value)); + else if (::strcmp(key, "TXLevel") == 0) + m_modemAX25TXLevel = m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = float(::atof(value)); + else if (::strcmp(key, "CWIdTXLevel") == 0) + m_modemCWIdTXLevel = float(::atof(value)); + else if (::strcmp(key, "D-StarTXLevel") == 0) + m_modemDStarTXLevel = float(::atof(value)); + else if (::strcmp(key, "DMRTXLevel") == 0) + m_modemDMRTXLevel = float(::atof(value)); + else if (::strcmp(key, "YSFTXLevel") == 0) + m_modemYSFTXLevel = float(::atof(value)); + else if (::strcmp(key, "P25TXLevel") == 0) + m_modemP25TXLevel = float(::atof(value)); + else if (::strcmp(key, "NXDNTXLevel") == 0) + m_modemNXDNTXLevel = float(::atof(value)); + else if (::strcmp(key, "POCSAGTXLevel") == 0) + m_modemPOCSAGTXLevel = float(::atof(value)); + else if (::strcmp(key, "FMTXLevel") == 0) + m_modemFMTXLevel = float(::atof(value)); + else if (::strcmp(key, "AX25TXLevel") == 0) + m_modemAX25TXLevel = float(::atof(value)); + else if (::strcmp(key, "RSSIMappingFile") == 0) + m_modemRSSIMappingFile = value; + else if (::strcmp(key, "Trace") == 0) + m_modemTrace = ::atoi(value) == 1; + else if (::strcmp(key, "Debug") == 0) + m_modemDebug = ::atoi(value) == 1; + } else if (section == SECTION_TRANSPARENT) { + if (::strcmp(key, "Enable") == 0) + m_transparentEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteAddress") == 0) + m_transparentRemoteAddress = value; + else if (::strcmp(key, "RemotePort") == 0) + m_transparentRemotePort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_transparentLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "SendFrameType") == 0) + m_transparentSendFrameType = (unsigned int)::atoi(value); + } else if (section == SECTION_UMP) { + if (::strcmp(key, "Enable") == 0) + m_umpEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_umpPort = value; + } else if (section == SECTION_DSTAR) { + if (::strcmp(key, "Enable") == 0) + m_dstarEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Module") == 0) { + // Convert the module to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_dstarModule = value; + } else if (::strcmp(key, "SelfOnly") == 0) + m_dstarSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "BlackList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + if (::strlen(p) > 0U) { + for (unsigned int i = 0U; p[i] != 0; i++) + p[i] = ::toupper(p[i]); + std::string callsign = std::string(p); + callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); + m_dstarBlackList.push_back(callsign); + } + p = ::strtok(NULL, ",\r\n"); } - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "AckReply") == 0) - m_dstarAckReply = ::atoi(value) == 1; - else if (::strcmp(key, "AckTime") == 0) - m_dstarAckTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckMessage") == 0) - m_dstarAckMessage = ::atoi(value) == 1; - else if (::strcmp(key, "ErrorReply") == 0) - m_dstarErrorReply = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_dstarRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "ModeHang") == 0) - m_dstarModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_DMR) { - if (::strcmp(key, "Enable") == 0) - m_dmrEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Beacons") == 0) - m_dmrBeacons = ::atoi(value) == 1 ? DMR_BEACONS_NETWORK : DMR_BEACONS_OFF; - else if (::strcmp(key, "BeaconInterval") == 0) { - m_dmrBeacons = m_dmrBeacons != DMR_BEACONS_OFF ? DMR_BEACONS_TIMED : DMR_BEACONS_OFF; - m_dmrBeaconInterval = (unsigned int)::atoi(value); - } else if (::strcmp(key, "BeaconDuration") == 0) - m_dmrBeaconDuration = (unsigned int)::atoi(value); - else if (::strcmp(key, "Id") == 0) - m_dmrId = (unsigned int)::atoi(value); - else if (::strcmp(key, "ColorCode") == 0) - m_dmrColorCode = (unsigned int)::atoi(value); - else if (::strcmp(key, "SelfOnly") == 0) - m_dmrSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "EmbeddedLCOnly") == 0) - m_dmrEmbeddedLCOnly = ::atoi(value) == 1; - else if (::strcmp(key, "DumpTAData") == 0) - m_dmrDumpTAData = ::atoi(value) == 1; - else if (::strcmp(key, "Prefixes") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int prefix = (unsigned int)::atoi(p); - if (prefix > 0U && prefix <= 999U) - m_dmrPrefixes.push_back(prefix); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "BlackList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrBlackList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "WhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "Slot1TGWhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrSlot1TGWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "Slot2TGWhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrSlot2TGWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "TXHang") == 0) - m_dmrTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallHang") == 0) - m_dmrCallHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_dmrModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "OVCM") == 0) - switch(::atoi(value)) { + } else if (::strcmp(key, "AckReply") == 0) + m_dstarAckReply = ::atoi(value) == 1; + else if (::strcmp(key, "AckTime") == 0) + m_dstarAckTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckMessage") == 0) + m_dstarAckMessage = ::atoi(value) == 1; + else if (::strcmp(key, "ErrorReply") == 0) + m_dstarErrorReply = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_dstarRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_dstarModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_DMR) { + if (::strcmp(key, "Enable") == 0) + m_dmrEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Beacons") == 0) + m_dmrBeacons = ::atoi(value) == 1 ? DMR_BEACONS_NETWORK : DMR_BEACONS_OFF; + else if (::strcmp(key, "BeaconInterval") == 0) { + m_dmrBeacons = m_dmrBeacons != DMR_BEACONS_OFF ? DMR_BEACONS_TIMED : DMR_BEACONS_OFF; + m_dmrBeaconInterval = (unsigned int)::atoi(value); + } else if (::strcmp(key, "BeaconDuration") == 0) + m_dmrBeaconDuration = (unsigned int)::atoi(value); + else if (::strcmp(key, "Id") == 0) + m_dmrId = (unsigned int)::atoi(value); + else if (::strcmp(key, "ColorCode") == 0) + m_dmrColorCode = (unsigned int)::atoi(value); + else if (::strcmp(key, "SelfOnly") == 0) + m_dmrSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "EmbeddedLCOnly") == 0) + m_dmrEmbeddedLCOnly = ::atoi(value) == 1; + else if (::strcmp(key, "DumpTAData") == 0) + m_dmrDumpTAData = ::atoi(value) == 1; + else if (::strcmp(key, "Prefixes") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int prefix = (unsigned int)::atoi(p); + if (prefix > 0U && prefix <= 999U) + m_dmrPrefixes.push_back(prefix); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "BlackList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrBlackList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "WhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "Slot1TGWhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrSlot1TGWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "Slot2TGWhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrSlot2TGWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "TXHang") == 0) + m_dmrTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallHang") == 0) + m_dmrCallHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_dmrModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "OVCM") == 0) { + switch (::atoi(value)) { case 1: m_dmrOVCM = DMR_OVCM_RX_ON; break; @@ -664,344 +679,362 @@ bool CConf::read() default: m_dmrOVCM = DMR_OVCM_OFF; break; + } } - } else if (section == SECTION_FUSION) { - if (::strcmp(key, "Enable") == 0) - m_fusionEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LowDeviation") == 0) - m_fusionLowDeviation = ::atoi(value) == 1; - else if (::strcmp(key, "DGID") == 0) { - m_fusionDGIdEnabled = true; - m_fusionDGId = (unsigned int)::atoi(value); - } else if (::strcmp(key, "RemoteGateway") == 0) - m_fusionRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "SelfOnly") == 0) - m_fusionSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_fusionTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_fusionModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_P25) { - if (::strcmp(key, "Enable") == 0) - m_p25Enabled = ::atoi(value) == 1; - else if (::strcmp(key, "Id") == 0) - m_p25Id = (unsigned int)::atoi(value); - else if (::strcmp(key, "NAC") == 0) - m_p25NAC = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "OverrideUIDCheck") == 0) - m_p25OverrideUID = ::atoi(value) == 1; - else if (::strcmp(key, "SelfOnly") == 0) - m_p25SelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_p25RemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_p25TXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_p25ModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_NXDN) { - if (::strcmp(key, "Enable") == 0) - m_nxdnEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Id") == 0) - m_nxdnId = (unsigned int)::atoi(value); - else if (::strcmp(key, "RAN") == 0) - m_nxdnRAN = (unsigned int)::atoi(value); - else if (::strcmp(key, "SelfOnly") == 0) - m_nxdnSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_nxdnRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_nxdnTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_nxdnModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_POCSAG) { - if (::strcmp(key, "Enable") == 0) - m_pocsagEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Frequency") == 0) - m_pocsagFrequency = (unsigned int)::atoi(value); - } else if (section == SECTION_FM) { - if (::strcmp(key, "Enable") == 0) - m_fmEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmCallsign = value; - } else if (::strcmp(key, "CallsignSpeed") == 0) - m_fmCallsignSpeed = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignFrequency") == 0) - m_fmCallsignFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignTime") == 0) - m_fmCallsignTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignHoldoff") == 0) - m_fmCallsignHoldoff = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignHighLevel") == 0) - m_fmCallsignHighLevel = float(::atof(value)); - else if (::strcmp(key, "CallsignLowLevel") == 0) - m_fmCallsignLowLevel = float(::atof(value)); - else if (::strcmp(key, "CallsignAtStart") == 0) - m_fmCallsignAtStart = ::atoi(value) == 1; - else if (::strcmp(key, "CallsignAtEnd") == 0) - m_fmCallsignAtEnd = ::atoi(value) == 1; - else if (::strcmp(key, "CallsignAtLatch") == 0) - m_fmCallsignAtLatch = ::atoi(value) == 1; - else if (::strcmp(key, "RFAck") == 0) { - // Convert the ack to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmRFAck = value; - } else if (::strcmp(key, "ExtAck") == 0) { - // Convert the ack to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmExtAck = value; - } else if (::strcmp(key, "AckSpeed") == 0) - m_fmAckSpeed = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckFrequency") == 0) - m_fmAckFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckMinTime") == 0) - m_fmAckMinTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckDelay") == 0) - m_fmAckDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckLevel") == 0) - m_fmAckLevel = float(::atof(value)); - else if (::strcmp(key, "Timeout") == 0) - m_fmTimeout = (unsigned int)::atoi(value); - else if (::strcmp(key, "TimeoutLevel") == 0) - m_fmTimeoutLevel = float(::atof(value)); - else if (::strcmp(key, "CTCSSFrequency") == 0) - m_fmCTCSSFrequency = float(::atof(value)); - else if (::strcmp(key, "CTCSSThreshold") == 0) - m_fmCTCSSHighThreshold = m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSHighThreshold") == 0) - m_fmCTCSSHighThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSLowThreshold") == 0) - m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSLevel") == 0) - m_fmCTCSSLevel = float(::atof(value)); - else if (::strcmp(key, "KerchunkTime") == 0) - m_fmKerchunkTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "HangTime") == 0) - m_fmHangTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "UseCOS") == 0) - m_fmUseCOS = ::atoi(value) == 1; - else if (::strcmp(key, "COSInvert") == 0) - m_fmCOSInvert = ::atoi(value) == 1; - else if (::strcmp(key, "RFAudioBoost") == 0) - m_fmRFAudioBoost = (unsigned int)::atoi(value); - else if (::strcmp(key, "MaxDevLevel") == 0) - m_fmMaxDevLevel = float(::atof(value)); - else if (::strcmp(key, "ExtAudioBoost") == 0) - m_fmExtAudioBoost = (unsigned int)::atoi(value); - } else if (section == SECTION_AX25) { - if (::strcmp(key, "Enable") == 0) - m_ax25Enabled = ::atoi(value) == 1; - else if (::strcmp(key, "RXTwist") == 0) - m_ax25RXTwist = ::atoi(value); - else if (::strcmp(key, "TXTwist") == 0) - m_ax25TXTwist = ::atoi(value); - else if (::strcmp(key, "Trace") == 0) - m_ax25Trace = ::atoi(value) == 1; - } else if (section == SECTION_DSTAR_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_dstarNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "GatewayAddress") == 0) - m_dstarGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_dstarGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_dstarLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_dstarNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_dstarNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_DMR_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_dmrNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Address") == 0) - m_dmrNetworkAddress = value; - else if (::strcmp(key, "Port") == 0) - m_dmrNetworkPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "Local") == 0) - m_dmrNetworkLocal = (unsigned int)::atoi(value); - else if (::strcmp(key, "Password") == 0) - m_dmrNetworkPassword = value; - else if (::strcmp(key, "Options") == 0) - m_dmrNetworkOptions = value; - else if (::strcmp(key, "Debug") == 0) - m_dmrNetworkDebug = ::atoi(value) == 1; - else if (::strcmp(key, "Jitter") == 0) - m_dmrNetworkJitter = (unsigned int)::atoi(value); - else if (::strcmp(key, "Slot1") == 0) - m_dmrNetworkSlot1 = ::atoi(value) == 1; - else if (::strcmp(key, "Slot2") == 0) - m_dmrNetworkSlot2 = ::atoi(value) == 1; - else if (::strcmp(key, "ModeHang") == 0) - m_dmrNetworkModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_FUSION_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_fusionNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LocalAddress") == 0) - m_fusionNetworkMyAddress = value; - else if (::strcmp(key, "LocalPort") == 0) - m_fusionNetworkMyPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_fusionNetworkGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_fusionNetworkGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_fusionNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_fusionNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_P25_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_p25NetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "GatewayAddress") == 0) - m_p25GatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_p25GatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_p25LocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_p25NetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_p25NetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_NXDN_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_nxdnNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Protocol") == 0) - m_nxdnNetworkProtocol = value; - else if (::strcmp(key, "LocalAddress") == 0) - m_nxdnLocalAddress = value; - else if (::strcmp(key, "LocalPort") == 0) - m_nxdnLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_nxdnGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_nxdnGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_nxdnNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_nxdnNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_POCSAG_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_pocsagNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LocalAddress") == 0) - m_pocsagLocalAddress = value; - else if (::strcmp(key, "LocalPort") == 0) - m_pocsagLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_pocsagGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_pocsagGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_pocsagNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_pocsagNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_AX25_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_ax25NetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Port") == 0) - m_ax25NetworkPort = value; - else if (::strcmp(key, "Speed") == 0) - m_ax25NetworkSpeed = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_ax25NetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_TFTSERIAL) { - if (::strcmp(key, "Port") == 0) - m_tftSerialPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_tftSerialBrightness = (unsigned int)::atoi(value); - } else if (section == SECTION_HD44780) { - if (::strcmp(key, "Rows") == 0) - m_hd44780Rows = (unsigned int)::atoi(value); - else if (::strcmp(key, "Columns") == 0) - m_hd44780Columns = (unsigned int)::atoi(value); - else if (::strcmp(key, "I2CAddress") == 0) - m_hd44780i2cAddress = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "PWM") == 0) - m_hd44780PWM = ::atoi(value) == 1; - else if (::strcmp(key, "PWMPin") == 0) - m_hd44780PWMPin = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMBright") == 0) - m_hd44780PWMBright = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMDim") == 0) - m_hd44780PWMDim = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_hd44780DisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_hd44780UTC = ::atoi(value) == 1; - else if (::strcmp(key, "Pins") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int pin = (unsigned int)::atoi(p); - m_hd44780Pins.push_back(pin); - p = ::strtok(NULL, ",\r\n"); + } else if (section == SECTION_FUSION) { + if (::strcmp(key, "Enable") == 0) + m_fusionEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LowDeviation") == 0) + m_fusionLowDeviation = ::atoi(value) == 1; + else if (::strcmp(key, "DGID") == 0) { + m_fusionDGIdEnabled = true; + m_fusionDGId = (unsigned int)::atoi(value); + } else if (::strcmp(key, "RemoteGateway") == 0) + m_fusionRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "SelfOnly") == 0) + m_fusionSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_fusionTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fusionModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_P25) { + if (::strcmp(key, "Enable") == 0) + m_p25Enabled = ::atoi(value) == 1; + else if (::strcmp(key, "Id") == 0) + m_p25Id = (unsigned int)::atoi(value); + else if (::strcmp(key, "NAC") == 0) + m_p25NAC = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "OverrideUIDCheck") == 0) + m_p25OverrideUID = ::atoi(value) == 1; + else if (::strcmp(key, "SelfOnly") == 0) + m_p25SelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_p25RemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_p25TXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_p25ModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_NXDN) { + if (::strcmp(key, "Enable") == 0) + m_nxdnEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Id") == 0) + m_nxdnId = (unsigned int)::atoi(value); + else if (::strcmp(key, "RAN") == 0) + m_nxdnRAN = (unsigned int)::atoi(value); + else if (::strcmp(key, "SelfOnly") == 0) + m_nxdnSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_nxdnRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_nxdnTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_nxdnModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_POCSAG) { + if (::strcmp(key, "Enable") == 0) + m_pocsagEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Frequency") == 0) + m_pocsagFrequency = (unsigned int)::atoi(value); + } else if (section == SECTION_FM) { + if (::strcmp(key, "Enable") == 0) + m_fmEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmCallsign = value; + } else if (::strcmp(key, "CallsignSpeed") == 0) + m_fmCallsignSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignFrequency") == 0) + m_fmCallsignFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignTime") == 0) + m_fmCallsignTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignHoldoff") == 0) + m_fmCallsignHoldoff = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignHighLevel") == 0) + m_fmCallsignHighLevel = float(::atof(value)); + else if (::strcmp(key, "CallsignLowLevel") == 0) + m_fmCallsignLowLevel = float(::atof(value)); + else if (::strcmp(key, "CallsignAtStart") == 0) + m_fmCallsignAtStart = ::atoi(value) == 1; + else if (::strcmp(key, "CallsignAtEnd") == 0) + m_fmCallsignAtEnd = ::atoi(value) == 1; + else if (::strcmp(key, "CallsignAtLatch") == 0) + m_fmCallsignAtLatch = ::atoi(value) == 1; + else if (::strcmp(key, "RFAck") == 0) { + // Convert the ack to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmRFAck = value; + } else if (::strcmp(key, "ExtAck") == 0) { + // Convert the ack to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmExtAck = value; + } else if (::strcmp(key, "AckSpeed") == 0) + m_fmAckSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckFrequency") == 0) + m_fmAckFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckMinTime") == 0) + m_fmAckMinTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckDelay") == 0) + m_fmAckDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckLevel") == 0) + m_fmAckLevel = float(::atof(value)); + else if (::strcmp(key, "Timeout") == 0) + m_fmTimeout = (unsigned int)::atoi(value); + else if (::strcmp(key, "TimeoutLevel") == 0) + m_fmTimeoutLevel = float(::atof(value)); + else if (::strcmp(key, "CTCSSFrequency") == 0) + m_fmCTCSSFrequency = float(::atof(value)); + else if (::strcmp(key, "CTCSSThreshold") == 0) + m_fmCTCSSHighThreshold = m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSHighThreshold") == 0) + m_fmCTCSSHighThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSLowThreshold") == 0) + m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSLevel") == 0) + m_fmCTCSSLevel = float(::atof(value)); + else if (::strcmp(key, "KerchunkTime") == 0) + m_fmKerchunkTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "KerchunkTX") == 0) + m_fmKerchunkTX = ::atoi(value) == 1; + else if (::strcmp(key, "HangTime") == 0) + m_fmHangTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "UseCOS") == 0) + m_fmUseCOS = ::atoi(value) == 1; + else if (::strcmp(key, "COSInvert") == 0) + m_fmCOSInvert = ::atoi(value) == 1; + else if (::strcmp(key, "RFAudioBoost") == 0) + m_fmRFAudioBoost = (unsigned int)::atoi(value); + else if (::strcmp(key, "MaxDevLevel") == 0) + m_fmMaxDevLevel = float(::atof(value)); + else if (::strcmp(key, "ExtAudioBoost") == 0) + m_fmExtAudioBoost = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fmModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_AX25) { + if (::strcmp(key, "Enable") == 0) + m_ax25Enabled = ::atoi(value) == 1; + else if (::strcmp(key, "RXTwist") == 0) + m_ax25RXTwist = ::atoi(value); + else if (::strcmp(key, "TXTwist") == 0) + m_ax25TXTwist = ::atoi(value); + else if (::strcmp(key, "Trace") == 0) + m_ax25Trace = ::atoi(value) == 1; + } else if (section == SECTION_DSTAR_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_dstarNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "GatewayAddress") == 0) + m_dstarGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_dstarGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_dstarLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_dstarNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_dstarNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_DMR_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_dmrNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Address") == 0) + m_dmrNetworkAddress = value; + else if (::strcmp(key, "Port") == 0) + m_dmrNetworkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Local") == 0) + m_dmrNetworkLocal = (unsigned int)::atoi(value); + else if (::strcmp(key, "Password") == 0) + m_dmrNetworkPassword = value; + else if (::strcmp(key, "Options") == 0) + m_dmrNetworkOptions = value; + else if (::strcmp(key, "Debug") == 0) + m_dmrNetworkDebug = ::atoi(value) == 1; + else if (::strcmp(key, "Jitter") == 0) + m_dmrNetworkJitter = (unsigned int)::atoi(value); + else if (::strcmp(key, "Slot1") == 0) + m_dmrNetworkSlot1 = ::atoi(value) == 1; + else if (::strcmp(key, "Slot2") == 0) + m_dmrNetworkSlot2 = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_dmrNetworkModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_FUSION_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_fusionNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_fusionNetworkMyAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_fusionNetworkMyPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_fusionNetworkGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_fusionNetworkGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fusionNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fusionNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_P25_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_p25NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "GatewayAddress") == 0) + m_p25GatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_p25GatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_p25LocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_p25NetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_p25NetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_NXDN_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_nxdnNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_nxdnLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_nxdnLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_nxdnGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_nxdnGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_nxdnNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_nxdnNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_POCSAG_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_pocsagNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_pocsagLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_pocsagLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_pocsagGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_pocsagGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_pocsagNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_pocsagNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_FM_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_fmNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_fmLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_fmLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_fmGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_fmGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fmNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fmNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_AX25_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_ax25NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_ax25NetworkPort = value; + else if (::strcmp(key, "Speed") == 0) + m_ax25NetworkSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_ax25NetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_TFTSERIAL) { + if (::strcmp(key, "Port") == 0) + m_tftSerialPort = value; + else if (::strcmp(key, "Brightness") == 0) + m_tftSerialBrightness = (unsigned int)::atoi(value); + } else if (section == SECTION_HD44780) { + if (::strcmp(key, "Rows") == 0) + m_hd44780Rows = (unsigned int)::atoi(value); + else if (::strcmp(key, "Columns") == 0) + m_hd44780Columns = (unsigned int)::atoi(value); + else if (::strcmp(key, "I2CAddress") == 0) + m_hd44780i2cAddress = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "PWM") == 0) + m_hd44780PWM = ::atoi(value) == 1; + else if (::strcmp(key, "PWMPin") == 0) + m_hd44780PWMPin = (unsigned int)::atoi(value); + else if (::strcmp(key, "PWMBright") == 0) + m_hd44780PWMBright = (unsigned int)::atoi(value); + else if (::strcmp(key, "PWMDim") == 0) + m_hd44780PWMDim = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_hd44780DisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_hd44780UTC = ::atoi(value) == 1; + else if (::strcmp(key, "Pins") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int pin = (unsigned int)::atoi(p); + m_hd44780Pins.push_back(pin); + p = ::strtok(NULL, ",\r\n"); + } } + } else if (section == SECTION_NEXTION) { + if (::strcmp(key, "Port") == 0) + m_nextionPort = value; + else if (::strcmp(key, "Brightness") == 0) + m_nextionIdleBrightness = m_nextionBrightness = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_nextionDisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_nextionUTC = ::atoi(value) == 1; + else if (::strcmp(key, "IdleBrightness") == 0) + m_nextionIdleBrightness = (unsigned int)::atoi(value); + else if (::strcmp(key, "ScreenLayout") == 0) + m_nextionScreenLayout = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayTempInFahrenheit") == 0) + m_nextionTempInFahrenheit = ::atoi(value) == 1; + } else if (section == SECTION_OLED) { + if (::strcmp(key, "Type") == 0) + m_oledType = (unsigned char)::atoi(value); + else if (::strcmp(key, "Brightness") == 0) + m_oledBrightness = (unsigned char)::atoi(value); + else if (::strcmp(key, "Invert") == 0) + m_oledInvert = ::atoi(value) == 1; + else if (::strcmp(key, "Scroll") == 0) + m_oledScroll = ::atoi(value) == 1; + else if (::strcmp(key, "Rotate") == 0) + m_oledRotate = ::atoi(value) == 1; + else if (::strcmp(key, "LogoScreensaver") == 0) + m_oledLogoScreensaver = ::atoi(value) == 1; + } else if (section == SECTION_LCDPROC) { + if (::strcmp(key, "Address") == 0) + m_lcdprocAddress = value; + else if (::strcmp(key, "Port") == 0) + m_lcdprocPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_lcdprocLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_lcdprocDisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_lcdprocUTC = ::atoi(value) == 1; + else if (::strcmp(key, "DimOnIdle") == 0) + m_lcdprocDimOnIdle = ::atoi(value) == 1; + } else if (section == SECTION_LOCK_FILE) { + if (::strcmp(key, "Enable") == 0) + m_lockFileEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "File") == 0) + m_lockFileName = value; + } else if (section == SECTION_MOBILE_GPS) { + if (::strcmp(key, "Enable") == 0) + m_mobileGPSEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Address") == 0) + m_mobileGPSAddress = value; + else if (::strcmp(key, "Port") == 0) + m_mobileGPSPort = (unsigned int)::atoi(value); + } else if (section == SECTION_REMOTE_CONTROL) { + if (::strcmp(key, "Enable") == 0) + m_remoteControlEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_remoteControlPort = (unsigned int)::atoi(value); } - } else if (section == SECTION_NEXTION) { - if (::strcmp(key, "Port") == 0) - m_nextionPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_nextionIdleBrightness = m_nextionBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_nextionDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_nextionUTC = ::atoi(value) == 1; - else if (::strcmp(key, "IdleBrightness") == 0) - m_nextionIdleBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "ScreenLayout") == 0) - m_nextionScreenLayout = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayTempInFahrenheit") == 0) - m_nextionTempInFahrenheit = ::atoi(value) == 1; - } else if (section == SECTION_OLED) { - if (::strcmp(key, "Type") == 0) - m_oledType = (unsigned char)::atoi(value); - else if (::strcmp(key, "Brightness") == 0) - m_oledBrightness = (unsigned char)::atoi(value); - else if (::strcmp(key, "Invert") == 0) - m_oledInvert = ::atoi(value) == 1; - else if (::strcmp(key, "Scroll") == 0) - m_oledScroll = ::atoi(value) == 1; - else if (::strcmp(key, "Rotate") == 0) - m_oledRotate = ::atoi(value) == 1; - else if (::strcmp(key, "LogoScreensaver") == 0) - m_oledLogoScreensaver = ::atoi(value) == 1; - } else if (section == SECTION_LCDPROC) { - if (::strcmp(key, "Address") == 0) - m_lcdprocAddress = value; - else if (::strcmp(key, "Port") == 0) - m_lcdprocPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_lcdprocLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_lcdprocDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_lcdprocUTC = ::atoi(value) == 1; - else if (::strcmp(key, "DimOnIdle") == 0) - m_lcdprocDimOnIdle = ::atoi(value) == 1; - } else if (section == SECTION_LOCK_FILE) { - if (::strcmp(key, "Enable") == 0) - m_lockFileEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "File") == 0) - m_lockFileName = value; - } else if (section == SECTION_MOBILE_GPS) { - if (::strcmp(key, "Enable") == 0) - m_mobileGPSEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Address") == 0) - m_mobileGPSAddress = value; - else if (::strcmp(key, "Port") == 0) - m_mobileGPSPort = (unsigned int)::atoi(value); - } else if (section == SECTION_REMOTE_CONTROL) { - if (::strcmp(key, "Enable") == 0) - m_remoteControlEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Port") == 0) - m_remoteControlPort = (unsigned int)::atoi(value); } - } - ::fclose(fp); + ::fclose(fp); - return true; + return true; } std::string CConf::getCallsign() const @@ -1144,6 +1177,11 @@ std::string CConf::getModemProtocol() const return m_modemProtocol; } +unsigned int CConf::getModemSpeed() const +{ + return m_modemSpeed; +} + unsigned int CConf::getModemAddress() const { return m_modemAddress; @@ -1689,6 +1727,11 @@ unsigned int CConf::getFMKerchunkTime() const return m_fmKerchunkTime; } +bool CConf::getFMKerchunkTX() const +{ + return m_fmKerchunkTX; +} + unsigned int CConf::getFMHangTime() const { return m_fmHangTime; @@ -1719,6 +1762,11 @@ unsigned int CConf::getFMExtAudioBoost() const return m_fmExtAudioBoost; } +unsigned int CConf::getFMModeHang() const +{ + return m_fmModeHang; +} + bool CConf::getAX25Enabled() const { return m_ax25Enabled; @@ -1964,6 +2012,41 @@ bool CConf::getPOCSAGNetworkDebug() const return m_pocsagNetworkDebug; } +bool CConf::getFMNetworkEnabled() const +{ + return m_fmNetworkEnabled; +} + +std::string CConf::getFMGatewayAddress() const +{ + return m_fmGatewayAddress; +} + +unsigned int CConf::getFMGatewayPort() const +{ + return m_fmGatewayPort; +} + +std::string CConf::getFMLocalAddress() const +{ + return m_fmLocalAddress; +} + +unsigned int CConf::getFMLocalPort() const +{ + return m_fmLocalPort; +} + +unsigned int CConf::getFMNetworkModeHang() const +{ + return m_fmNetworkModeHang; +} + +bool CConf::getFMNetworkDebug() const +{ + return m_fmNetworkDebug; +} + bool CConf::getAX25NetworkEnabled() const { return m_ax25NetworkEnabled; @@ -2104,7 +2187,6 @@ bool CConf::getOLEDLogoScreensaver() const return m_oledLogoScreensaver; } - std::string CConf::getLCDprocAddress() const { return m_lcdprocAddress; diff --git a/Conf.h b/Conf.h index 335c1e3..c3c0c16 100644 --- a/Conf.h +++ b/Conf.h @@ -71,6 +71,7 @@ public: // The Modem section std::string getModemPort() const; std::string getModemProtocol() const; + unsigned int getModemSpeed() const; unsigned int getModemAddress() const; bool getModemRXInvert() const; bool getModemTXInvert() const; @@ -204,12 +205,14 @@ public: unsigned int getFMCTCSSLowThreshold() const; float getFMCTCSSLevel() const; unsigned int getFMKerchunkTime() const; + bool getFMKerchunkTX() const; unsigned int getFMHangTime() const; bool getFMUseCOS() const; bool getFMCOSInvert() const; unsigned int getFMRFAudioBoost() const; float getFMMaxDevLevel() const; unsigned int getFMExtAudioBoost() const; + unsigned int getFMModeHang() const; // The D-Star Network section bool getDStarNetworkEnabled() const; @@ -268,6 +271,15 @@ public: unsigned int getPOCSAGNetworkModeHang() const; bool getPOCSAGNetworkDebug() const; + // The FM Network section + bool getFMNetworkEnabled() const; + std::string getFMGatewayAddress() const; + unsigned int getFMGatewayPort() const; + std::string getFMLocalAddress() const; + unsigned int getFMLocalPort() const; + unsigned int getFMNetworkModeHang() const; + bool getFMNetworkDebug() const; + // The AX.25 Network section bool getAX25NetworkEnabled() const; std::string getAX25NetworkPort() const; @@ -364,6 +376,7 @@ private: std::string m_modemPort; std::string m_modemProtocol; + unsigned int m_modemSpeed; unsigned int m_modemAddress; bool m_modemRXInvert; bool m_modemTXInvert; @@ -487,12 +500,14 @@ private: unsigned int m_fmCTCSSLowThreshold; float m_fmCTCSSLevel; unsigned int m_fmKerchunkTime; + bool m_fmKerchunkTX; unsigned int m_fmHangTime; bool m_fmUseCOS; bool m_fmCOSInvert; unsigned int m_fmRFAudioBoost; float m_fmMaxDevLevel; unsigned int m_fmExtAudioBoost; + unsigned int m_fmModeHang; bool m_dstarNetworkEnabled; std::string m_dstarGatewayAddress; @@ -545,6 +560,14 @@ private: unsigned int m_pocsagNetworkModeHang; bool m_pocsagNetworkDebug; + bool m_fmNetworkEnabled; + std::string m_fmGatewayAddress; + unsigned int m_fmGatewayPort; + std::string m_fmLocalAddress; + unsigned int m_fmLocalPort; + unsigned int m_fmNetworkModeHang; + bool m_fmNetworkDebug; + bool m_ax25NetworkEnabled; std::string m_ax25NetworkPort; unsigned int m_ax25NetworkSpeed; diff --git a/Display.cpp b/Display.cpp index d0e6fd0..22bc953 100644 --- a/Display.cpp +++ b/Display.cpp @@ -511,7 +511,7 @@ CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) if (port == "modem") serial = new CModemSerialPort(modem); else - serial = new CSerialController(port, (type == "TFT Serial") ? SERIAL_9600 : SERIAL_115200); + serial = new CSerialController(port, (type == "TFT Serial") ? 9600U : 115200U); if (type == "TFT Surenoo") display = new CTFTSurenoo(conf.getCallsign(), dmrid, serial, brightness, conf.getDuplex()); @@ -565,11 +565,11 @@ CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) display = new CNullDisplay; } } else { - SERIAL_SPEED baudrate = SERIAL_9600; - if (screenLayout&0x0cU) - baudrate = SERIAL_115200; + unsigned int baudrate = 9600U; + if (screenLayout == 4U) + baudrate = 115200U; - LogInfo(" Display baudrate: %u ",baudrate); + LogInfo(" Display baudrate: %u ", baudrate); ISerialPort* serial = new CSerialController(port, baudrate); display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF, conf.getLocation()); } diff --git a/FMControl.cpp b/FMControl.cpp new file mode 100644 index 0000000..8a8f133 --- /dev/null +++ b/FMControl.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "FMControl.h" + +#include + +#if defined(DUMP_RF_AUDIO) +#include +#endif + +#define SWAP_BYTES_16(a) (((a >> 8) & 0x00FFU) | ((a << 8) & 0xFF00U)) + +const float DEEMPHASIS_GAIN_DB = 0.0F; +const float PREEMPHASIS_GAIN_DB = 13.0F; +const float FILTER_GAIN_DB = 0.0F; +const unsigned int FM_MASK = 0x00000FFFU; + +CFMControl::CFMControl(CFMNetwork* network) : +m_network(network), +m_enabled(false), +m_incomingRFAudio(1600U, "Incoming RF FM Audio"), +m_preemphasis (NULL), +m_deemphasis (NULL), +m_filterStage1(NULL), +m_filterStage2(NULL), +m_filterStage3(NULL) +{ + m_preemphasis = new CIIRDirectForm1Filter(8.315375384336983F,-7.03334621603483F,0.0F,1.0F,0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); + m_deemphasis = new CIIRDirectForm1Filter(0.07708787090460224F,0.07708787090460224F,0.0F,1.0F,-0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); + + //cheby type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 + m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB); + m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB); + m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB); +} + +CFMControl::~CFMControl() +{ + delete m_preemphasis ; + delete m_deemphasis ; + + delete m_filterStage1; + delete m_filterStage2; + delete m_filterStage3; +} + +bool CFMControl::writeModem(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (m_network == NULL) + return true; + + if (data[0U] == TAG_HEADER) + return true; + + if (data[0U] == TAG_EOT) + return m_network->writeEOT(); + + if (data[0U] != TAG_DATA) + return false; + + m_incomingRFAudio.addData(data + 1U, length - 1U); + unsigned int bufferLength = m_incomingRFAudio.dataSize(); + if (bufferLength > 252U)//168 samples 12-bit + bufferLength = 252U; + + if (bufferLength >= 3U) { + bufferLength = bufferLength - bufferLength % 3U; //round down to nearest multiple of 3 + unsigned char bufferData[252U]; + m_incomingRFAudio.getData(bufferData, bufferLength); + + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; + unsigned short out[168U]; + unsigned int nOut = 0U; + short unpackedSamples[2U]; + + for (unsigned int i = 0U; i < bufferLength; i += 3U) { + //extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed + packPointer[0U] = bufferData[i]; + packPointer[1U] = bufferData[i + 1U]; + packPointer[2U] = bufferData[i + 2U]; + unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048); + unpackedSamples[0U] = short(int(pack >> 12) - 2048); + + //process unpacked sample pair + for(unsigned char j = 0U; j < 2U; j++) { + //Convert to float (-1.0 to +1.0) + float sampleFloat = float(unpackedSamples[j]) / 2048.0F; + + //De-emphasise and remove CTCSS + sampleFloat = m_deemphasis->filter(sampleFloat); + sampleFloat = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat))); + + // Repack the float data to 16 bit unsigned + unsigned short sampleUShort = (unsigned short)((sampleFloat + 1.0F) * 32767.0F + 0.5F); + out[nOut++] = SWAP_BYTES_16(sampleUShort); + } + } + +#if defined(DUMP_RF_AUDIO) + FILE * audiofile = fopen("./audiodump.bin", "ab"); + if(audiofile != NULL) { + fwrite(out, sizeof(unsigned short), nOut, audiofile); + fclose(audiofile); + } +#endif + return m_network->writeData((unsigned char*)out, nOut * sizeof(unsigned short)); + } + + return true; +} + +unsigned int CFMControl::readModem(unsigned char* data, unsigned int space) +{ + assert(data != NULL); + assert(space > 0U); + + if (m_network == NULL) + return 0U; + + if (space > 252U) + space = 252U; + + unsigned short netData[168U];//modem can handle up to 168 samples at a time + unsigned int length = m_network->read((unsigned char*)netData, 168U * sizeof(unsigned short)); + length /= sizeof(unsigned short); + if (length == 0U) + return 0U; + + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; + unsigned int nData = 0U; + + for(unsigned int i = 0; i < length; i++) { + unsigned short netSample = SWAP_BYTES_16(netData[i]); + + // Convert the unsigned 16-bit data (+65535 - 0) to float (+1.0 - -1.0) + float sampleFloat = (float(netSample) / 32768.0F) - 1.0F; + + //preemphasis + sampleFloat = m_preemphasis->filter(sampleFloat); + + // Convert float to 12-bit samples (0 to 4095) + unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F); + + // pack 2 samples onto 3 bytes + if((i & 1U) == 0) { + pack = 0U; + pack = sample12bit << 12; + } else { + pack |= sample12bit; + + data[nData++] = packPointer[0U]; + data[nData++] = packPointer[1U]; + data[nData++] = packPointer[2U]; + } + } + + return nData; +} + +void CFMControl::clock(unsigned int ms) +{ + // May not be needed +} + +void CFMControl::enable(bool enabled) +{ + // May not be needed +} diff --git a/FMControl.h b/FMControl.h new file mode 100644 index 0000000..1647010 --- /dev/null +++ b/FMControl.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(FMControl_H) +#define FMControl_H + +#include "FMNetwork.h" +#include "Defines.h" +#include "IIRDirectForm1Filter.h" + +// Uncomment this to dump audio to a raw audio file +// The file will be written in same folder as executable +// Toplay the file : ffplay -autoexit -f u16be -ar 8000 audiodump.bin +// #define DUMP_RF_AUDIO + +class CFMControl { +public: + CFMControl(CFMNetwork* network); + ~CFMControl(); + + bool writeModem(const unsigned char* data, unsigned int length); + + unsigned int readModem(unsigned char* data, unsigned int space); + + void clock(unsigned int ms); + + void enable(bool enabled); + +private: + CFMNetwork* m_network; + bool m_enabled; + CRingBuffer m_incomingRFAudio; + CIIRDirectForm1Filter * m_preemphasis; + CIIRDirectForm1Filter * m_deemphasis; + CIIRDirectForm1Filter * m_filterStage1; + CIIRDirectForm1Filter * m_filterStage2; + CIIRDirectForm1Filter * m_filterStage3; +}; + +#endif diff --git a/FMNetwork.cpp b/FMNetwork.cpp new file mode 100644 index 0000000..67d0f2e --- /dev/null +++ b/FMNetwork.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "FMNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 500U; + +CFMNetwork::CFMNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : +m_socket(localAddress, localPort), +m_address(), +m_port(gatewayPort), +m_debug(debug), +m_enabled(false), +m_buffer(2000U, "FM Network"), +m_pollTimer(1000U, 5U) +{ + assert(gatewayPort > 0U); + assert(!gatewayAddress.empty()); + + m_address = CUDPSocket::lookup(gatewayAddress); +} + +CFMNetwork::~CFMNetwork() +{ +} + +bool CFMNetwork::open() +{ + LogMessage("Opening FM network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + m_pollTimer.start(); + + return m_socket.open(); +} + +bool CFMNetwork::writeData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + unsigned char buffer[500U]; + ::memset(buffer, 0x00U, 500U); + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'D'; + + ::memcpy(buffer + 3U, data, length); + + if (m_debug) + CUtils::dump(1U, "FM Network Data Sent", buffer, length + 3U); + + return m_socket.write(buffer, length + 3U, m_address, m_port); +} + +bool CFMNetwork::writeEOT() +{ + unsigned char buffer[10U]; + ::memset(buffer, 0x00U, 10U); + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'E'; + + if (m_debug) + CUtils::dump(1U, "FM Network End of Transmission Sent", buffer, 3U); + + return m_socket.write(buffer, 3U, m_address, m_port); +} + +void CFMNetwork::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + if (m_pollTimer.hasExpired()) { + writePoll(); + m_pollTimer.start(); + } + + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || port != m_port) { + LogMessage("FM packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port); + return; + } + + // Ignore incoming polls + if (::memcmp(buffer, "FMP", 3U) == 0) + return; + + // Invalid packet type? + if (::memcmp(buffer, "FMD", 3U) != 0) + return; + + if (!m_enabled) + return; + + if (m_debug) + CUtils::dump(1U, "FM Network Data Received", buffer, length); + + m_buffer.addData(buffer + 3U, length - 3U); +} + +unsigned int CFMNetwork::read(unsigned char* data, unsigned int space) +{ + assert(data != NULL); + + + unsigned int bytes = m_buffer.dataSize(); + if (bytes == 0U) + return 0U; + + if (bytes < space) + space = bytes; + + //we store usignedshorts, therefore ensure we always return and even number of data + if(space > 0 && space % 2 != 0) + space--;//round down to multiple of 2 + + m_buffer.getData(data, space); + + return space; +} + +void CFMNetwork::reset() +{ +} + +void CFMNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing FM network connection"); +} + +void CFMNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + else if (!enabled && m_enabled) + m_buffer.clear(); + + m_enabled = enabled; +} + +bool CFMNetwork::writePoll() +{ + unsigned char buffer[3U]; + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'P'; + + if (m_debug) + CUtils::dump(1U, "FM Network Poll Sent", buffer, 3U); + + return m_socket.write(buffer, 3U, m_address, m_port); +} diff --git a/FMNetwork.h b/FMNetwork.h new file mode 100644 index 0000000..453e776 --- /dev/null +++ b/FMNetwork.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef FMNetwork_H +#define FMNetwork_H + +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CFMNetwork { +public: + CFMNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CFMNetwork(); + + bool open(); + + void enable(bool enabled); + + bool writeData(const unsigned char* data, unsigned int length); + + bool writeEOT(); + + unsigned int read(unsigned char* data, unsigned int space); + + void reset(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + bool m_debug; + bool m_enabled; + CRingBuffer m_buffer; + CTimer m_pollTimer; + + bool writePoll(); +}; + +#endif diff --git a/I2CController.cpp b/I2CController.cpp index 247129c..9886443 100644 --- a/I2CController.cpp +++ b/I2CController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2011,2013,2014-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -17,6 +17,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#if defined(__linux__) + #include "I2CController.h" #include "Log.h" @@ -24,52 +26,18 @@ #include #include - -#if defined(_WIN32) || defined(_WIN64) - -#include -#include - -CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : -CSerialController(device, speed, assertRTS), -m_address(address) -{ -} - -CI2CController::~CI2CController() -{ -} - -bool CI2CController::open() -{ - return CSerialController::open(); -} - -int CI2CController::read(unsigned char* buffer, unsigned int length) -{ - return CSerialController::read(buffer, length); -} - -int CI2CController::write(const unsigned char* buffer, unsigned int length) -{ - return CSerialController::write(buffer, length); -} - -#else - #include #include #include #include #include #include -#if defined(__linux__) #include -#endif -CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : -CSerialController(device, speed, assertRTS), -m_address(address) +CI2CController::CI2CController(const std::string& device, unsigned int address) : +m_device(device), +m_address(address), +m_fd(-1) { } @@ -81,7 +49,6 @@ bool CI2CController::open() { assert(m_fd == -1); -#if defined(__linux__) m_fd = ::open(m_device.c_str(), O_RDWR); if (m_fd < 0) { LogError("Cannot open device - %s", m_device.c_str()); @@ -89,19 +56,16 @@ bool CI2CController::open() } if (::ioctl(m_fd, I2C_TENBIT, 0) < 0) { - LogError("CI2C: failed to set 7bitaddress"); + LogError("I2C: failed to set 7bitaddress"); ::close(m_fd); return false; } if (::ioctl(m_fd, I2C_SLAVE, m_address) < 0) { - LogError("CI2C: Failed to acquire bus access/talk to slave 0x%02X", m_address); + LogError("I2C: Failed to acquire bus access/talk to slave 0x%02X", m_address); ::close(m_fd); return false; } -#else - #warning "I2C controller supports Linux only" -#endif return true; } @@ -117,7 +81,6 @@ int CI2CController::read(unsigned char* buffer, unsigned int length) unsigned int offset = 0U; while (offset < length) { -#if defined(__linux__) ssize_t n = ::read(m_fd, buffer + offset, 1U); if (n < 0) { if (errno != EAGAIN) { @@ -128,7 +91,6 @@ int CI2CController::read(unsigned char* buffer, unsigned int length) if (n > 0) offset += n; -#endif } return length; @@ -144,10 +106,7 @@ int CI2CController::write(const unsigned char* buffer, unsigned int length) unsigned int ptr = 0U; while (ptr < length) { - ssize_t n = 0U; -#if defined(__linux__) - n = ::write(m_fd, buffer + ptr, 1U); -#endif + ssize_t n = ::write(m_fd, buffer + ptr, 1U); if (n < 0) { if (errno != EAGAIN) { LogError("Error returned from write(), errno=%d", errno); @@ -162,4 +121,12 @@ int CI2CController::write(const unsigned char* buffer, unsigned int length) return length; } +void CI2CController::close() +{ + assert(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + #endif diff --git a/I2CController.h b/I2CController.h index 6e59672..281c587 100644 --- a/I2CController.h +++ b/I2CController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -20,11 +20,15 @@ #ifndef I2CController_H #define I2CController_H -#include "SerialController.h" +#if defined(__linux__) -class CI2CController : public CSerialController { +#include "SerialPort.h" + +#include + +class CI2CController : public ISerialPort { public: - CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address = 0x22U, bool assertRTS = false); + CI2CController(const std::string& device, unsigned int address = 0x22U); virtual ~CI2CController(); virtual bool open(); @@ -33,8 +37,14 @@ public: virtual int write(const unsigned char* buffer, unsigned int length); + virtual void close(); + private: + std::string m_device; unsigned int m_address; + int m_fd; }; #endif + +#endif diff --git a/IIRDirectForm1Filter.cpp b/IIRDirectForm1Filter.cpp new file mode 100644 index 0000000..946acdd --- /dev/null +++ b/IIRDirectForm1Filter.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA + * + * 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 "IIRDirectForm1Filter.h" +#include "math.h" + +CIIRDirectForm1Filter::CIIRDirectForm1Filter(float b0, float b1, float b2, float , float a1, float a2, float addtionalGaindB) : +m_x2(0.0F), +m_y2(0.0F), +m_x1(0.0F), +m_y1(0.0F), +m_b0(b0), +m_b1(b1), +m_b2(b2), +m_a1(a1), +m_a2(a2), +m_additionalGainLin(0.0F) +{ + m_additionalGainLin = ::powf(10.0F, addtionalGaindB / 20.0F); +} + +float CIIRDirectForm1Filter::filter(float sample) +{ + float output = m_b0 * sample + + m_b1 * m_x1 + + m_b2 * m_x2 + - m_a1 * m_y1 + - m_a2 * m_y2; + + m_x2 = m_x1; + m_y2 = m_y1; + m_x1 = sample; + m_y1 = output; + + return output * m_additionalGainLin; +} + +void CIIRDirectForm1Filter::reset() +{ + m_x1 = 0.0f; + m_x2 = 0.0f; + m_y1 = 0.0f; + m_y2 = 0.0f; +} diff --git a/IIRDirectForm1Filter.h b/IIRDirectForm1Filter.h new file mode 100644 index 0000000..f575f7f --- /dev/null +++ b/IIRDirectForm1Filter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(IIRDIRECTFORM1FILTER_H) +#define IIRDIRECTFORM1FILTER_H + +class CIIRDirectForm1Filter +{ +public: + CIIRDirectForm1Filter(float b0, float b1, float b2, float, float a1, float a2, float additionalGaindB); + float filter(float sample); + void reset(); + +private: +// delay line + float m_x2; // x[n-2] + float m_y2; // y[n-2] + float m_x1; // x[n-1] + float m_y1; // y[n-1] + + // coefficients + // FIR + float m_b0; + float m_b1; + float m_b2; + // IIR + float m_a1; + float m_a2; + + float m_additionalGainLin; +}; + + +#endif \ No newline at end of file diff --git a/MMDVM.ini b/MMDVM.ini index 6b12fff..0ae670f 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -45,6 +45,7 @@ Time=24 # Port=/dev/ttyAMA0 Port=\\.\COM4 Protocol=uart +Speed=115200 # Address=0x22 TXInvert=1 RXInvert=0 @@ -169,12 +170,14 @@ CTCSSThreshold=30 # CTCSSLowThreshold=20 CTCSSLevel=20 KerchunkTime=0 +KerchunkTX=1 HangTime=7 UseCOS=1 COSInvert=0 RFAudioBoost=1 MaxDevLevel=90 ExtAudioBoost=1 +# ModeHang=10 [AX.25] Enable=1 @@ -239,6 +242,15 @@ GatewayPort=4800 # ModeHang=3 Debug=0 +[FM Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=3810 +GatewayAddress=127.0.0.1 +GatewayPort=4810 +# ModeHang=3 +Debug=0 + [AX.25 Network] Enable=1 Port=/dev/ttyp7 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 6c10567..8e95fec 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -121,6 +121,7 @@ m_ysf(NULL), m_p25(NULL), m_nxdn(NULL), m_pocsag(NULL), +m_fm(NULL), m_ax25(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), @@ -128,6 +129,7 @@ m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), m_pocsagNetwork(NULL), +m_fmNetwork(NULL), m_ax25Network(NULL), m_display(NULL), m_ump(NULL), @@ -137,12 +139,14 @@ m_dmrRFModeHang(10U), m_ysfRFModeHang(10U), m_p25RFModeHang(10U), m_nxdnRFModeHang(10U), +m_fmRFModeHang(10U), m_dstarNetModeHang(3U), m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), m_pocsagNetModeHang(3U), +m_fmNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), @@ -325,6 +329,12 @@ int CMMDVMHost::run() return 1; } + if (m_fmEnabled && m_conf.getFMNetworkEnabled()) { + ret = createFMNetwork(); + if (!ret) + return 1; + } + if (m_ax25Enabled && m_conf.getAX25NetworkEnabled()) { ret = createAX25Network(); if (!ret) @@ -638,6 +648,12 @@ int CMMDVMHost::run() m_ax25 = new CAX25Control(m_ax25Network, trace); } + if (m_fmEnabled) { + m_fmRFModeHang = m_conf.getFMModeHang(); + + m_fm = new CFMControl(m_fmNetwork); + } + bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { unsigned int port = m_conf.getRemoteControlPort(); @@ -675,12 +691,6 @@ int CMMDVMHost::run() else if (!error && m_mode == MODE_ERROR) setMode(MODE_IDLE); - unsigned char mode = m_modem->getMode(); - if (mode == MODE_FM && m_mode != MODE_FM) - setMode(mode); - else if (mode != MODE_FM && m_mode == MODE_FM) - setMode(mode); - if (m_ump != NULL) { bool tx = m_modem->hasTX(); m_ump->setTX(tx); @@ -830,11 +840,28 @@ int CMMDVMHost::run() } } + len = m_modem->readFMData(data); + if (m_fm != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = m_fm->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_fmRFModeHang); + setMode(MODE_FM); + } + } else if (m_mode == MODE_FM) { + m_fm->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM modem data received when in mode %u", m_mode); + } + } + len = m_modem->readAX25Data(data); if (m_ax25 != NULL && len > 0U) { if (m_mode == MODE_IDLE || m_mode == MODE_FM) { m_ax25->writeModem(data, len); - } else if (m_mode != MODE_LOCKOUT) { + } + else if (m_mode != MODE_LOCKOUT) { LogWarning("NXDN modem data received when in mode %u", m_mode); } } @@ -989,6 +1016,25 @@ int CMMDVMHost::run() } } + if (m_fm != NULL) { + unsigned int space = m_modem->getFMSpace(); + if (space > 0U) { + len = m_fm->readModem(data, space); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_fmNetModeHang); + setMode(MODE_FM); + } + if (m_mode == MODE_FM) { + m_modem->writeFMData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM data received when in mode %u", m_mode); + } + } + } + } + if (m_ax25 != NULL) { ret = m_modem->hasAX25Space(); if (ret) { @@ -996,7 +1042,8 @@ int CMMDVMHost::run() if (len > 0U) { if (m_mode == MODE_IDLE || m_mode == MODE_FM) { m_modem->writeAX25Data(data, len); - } else if (m_mode != MODE_LOCKOUT) { + } + else if (m_mode != MODE_LOCKOUT) { LogWarning("AX.25 data received when in mode %u", m_mode); } } @@ -1035,6 +1082,8 @@ int CMMDVMHost::run() m_nxdn->clock(ms); if (m_pocsag != NULL) m_pocsag->clock(ms); + if (m_fm != NULL) + m_fm->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); @@ -1048,6 +1097,8 @@ int CMMDVMHost::run() m_nxdnNetwork->clock(ms); if (m_pocsagNetwork != NULL) m_pocsagNetwork->clock(ms); + if (m_fmNetwork != NULL) + m_fmNetwork->clock(ms); if (m_mobileGPS != NULL) m_mobileGPS->clock(ms); @@ -1173,6 +1224,11 @@ int CMMDVMHost::run() delete m_pocsagNetwork; } + if (m_fmNetwork != NULL) { + m_fmNetwork->close(); + delete m_fmNetwork; + } + if (m_ax25Network != NULL) { m_ax25Network->close(); delete m_ax25Network; @@ -1194,6 +1250,7 @@ int CMMDVMHost::run() delete m_p25; delete m_nxdn; delete m_pocsag; + delete m_fm; delete m_ax25; return 0; @@ -1203,6 +1260,7 @@ bool CMMDVMHost::createModem() { std::string port = m_conf.getModemPort(); std::string protocol = m_conf.getModemProtocol(); + unsigned int speed = m_conf.getModemSpeed(); unsigned int address = m_conf.getModemAddress(); bool rxInvert = m_conf.getModemRXInvert(); bool txInvert = m_conf.getModemTXInvert(); @@ -1239,9 +1297,13 @@ bool CMMDVMHost::createModem() LogInfo("Modem Parameters"); LogInfo(" Port: %s", port.c_str()); +#if defined(__linux__) LogInfo(" Protocol: %s", protocol.c_str()); if (protocol == "i2c") - LogInfo(" i2c Address: %02X", address); + LogInfo(" I2C Address: %02X", address); + else +#endif + LogInfo(" Speed: %u", speed); LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); @@ -1265,7 +1327,7 @@ bool CMMDVMHost::createModem() LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); m_modem = CModem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug); - m_modem->setSerialParams(protocol,address); + m_modem->setSerialParams(protocol, address, speed); m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, ax25TXLevel); m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency); @@ -1287,7 +1349,6 @@ bool CMMDVMHost::createModem() bool callsignAtEnd = m_conf.getFMCallsignAtEnd(); bool callsignAtLatch = m_conf.getFMCallsignAtLatch(); std::string rfAck = m_conf.getFMRFAck(); - std::string extAck = m_conf.getFMExtAck(); unsigned int ackSpeed = m_conf.getFMAckSpeed(); unsigned int ackFrequency = m_conf.getFMAckFrequency(); unsigned int ackMinTime = m_conf.getFMAckMinTime(); @@ -1300,12 +1361,13 @@ bool CMMDVMHost::createModem() unsigned int ctcssLowThreshold = m_conf.getFMCTCSSLowThreshold(); float ctcssLevel = m_conf.getFMCTCSSLevel(); unsigned int kerchunkTime = m_conf.getFMKerchunkTime(); + bool kerchunkTX = m_conf.getFMKerchunkTX(); unsigned int hangTime = m_conf.getFMHangTime(); bool useCOS = m_conf.getFMUseCOS(); bool cosInvert = m_conf.getFMCOSInvert(); unsigned int rfAudioBoost = m_conf.getFMRFAudioBoost(); float maxDevLevel = m_conf.getFMMaxDevLevel(); - unsigned int extAudioBoost = m_conf.getFMExtAudioBoost(); + unsigned int modeHangTime = m_conf.getFMModeHang(); LogInfo("FM Parameters"); LogInfo(" Callsign: %s", callsign.c_str()); @@ -1319,7 +1381,6 @@ bool CMMDVMHost::createModem() LogInfo(" Callsign At End: %s", callsignAtEnd ? "yes" : "no"); LogInfo(" Callsign At Latch: %s", callsignAtLatch ? "yes" : "no"); LogInfo(" RF Ack: %s", rfAck.c_str()); - // LogInfo(" Ext. Ack: %s", extAck.c_str()); LogInfo(" Ack Speed: %uWPM", ackSpeed); LogInfo(" Ack Frequency: %uHz", ackFrequency); LogInfo(" Ack Min Time: %us", ackMinTime); @@ -1332,16 +1393,27 @@ bool CMMDVMHost::createModem() LogInfo(" CTCSS Low Threshold: %u", ctcssLowThreshold); LogInfo(" CTCSS Level: %.1f%%", ctcssLevel); LogInfo(" Kerchunk Time: %us", kerchunkTime); + LogInfo(" Kerchunk TX: %s", kerchunkTX ? "yes" : "no"); LogInfo(" Hang Time: %us", hangTime); LogInfo(" Use COS: %s", useCOS ? "yes" : "no"); LogInfo(" COS Invert: %s", cosInvert ? "yes" : "no"); LogInfo(" RF Audio Boost: x%u", rfAudioBoost); LogInfo(" Max. Deviation Level: %.1f%%", maxDevLevel); - // LogInfo(" Ext. Audio Boost: x%u", extAudioBoost); + LogInfo(" Mode Hang: %us", modeHangTime); m_modem->setFMCallsignParams(callsign, callsignSpeed, callsignFrequency, callsignTime, callsignHoldoff, callsignHighLevel, callsignLowLevel, callsignAtStart, callsignAtEnd, callsignAtLatch); m_modem->setFMAckParams(rfAck, ackSpeed, ackFrequency, ackMinTime, ackDelay, ackLevel); - m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, cosInvert, rfAudioBoost, maxDevLevel); + m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, kerchunkTX, hangTime, useCOS, cosInvert, rfAudioBoost, maxDevLevel); + + if (m_conf.getFMNetworkEnabled()) { + std::string extAck = m_conf.getFMExtAck(); + unsigned int extAudioBoost = m_conf.getFMExtAudioBoost(); + + LogInfo(" Ext. Ack: %s", extAck.c_str()); + LogInfo(" Ext. Audio Boost: x%u", extAudioBoost); + + m_modem->setFMExtParams(extAck, extAudioBoost); + } } bool ret = m_modem->open(); @@ -1594,11 +1666,41 @@ bool CMMDVMHost::createPOCSAGNetwork() return true; } +bool CMMDVMHost::createFMNetwork() +{ + std::string gatewayAddress = m_conf.getFMGatewayAddress(); + unsigned int gatewayPort = m_conf.getFMGatewayPort(); + std::string localAddress = m_conf.getFMLocalAddress(); + unsigned int localPort = m_conf.getFMLocalPort(); + m_fmNetModeHang = m_conf.getFMNetworkModeHang(); + bool debug = m_conf.getFMNetworkDebug(); + + LogInfo("FM Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Address: %s", localAddress.c_str()); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Mode Hang: %us", m_fmNetModeHang); + + m_fmNetwork = new CFMNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + + bool ret = m_fmNetwork->open(); + if (!ret) { + delete m_fmNetwork; + m_fmNetwork = NULL; + return false; + } + + m_fmNetwork->enable(true); + + return true; +} + bool CMMDVMHost::createAX25Network() { - std::string port = m_conf.getAX25NetworkPort(); + std::string port = m_conf.getAX25NetworkPort(); unsigned int speed = m_conf.getAX25NetworkSpeed(); - bool debug = m_conf.getAX25NetworkDebug(); + bool debug = m_conf.getAX25NetworkDebug(); LogInfo("AX.25 Network Parameters"); LogInfo(" Port: %s", port.c_str()); @@ -1667,6 +1769,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1681,6 +1785,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_DSTAR); @@ -1705,6 +1811,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1719,6 +1827,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_DMR); @@ -1747,6 +1857,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1761,6 +1873,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_YSF); @@ -1785,6 +1899,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1799,6 +1915,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_P25); @@ -1823,6 +1941,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1837,6 +1957,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(true); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_NXDN); @@ -1861,6 +1983,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1875,6 +1999,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(true); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); m_modem->setMode(MODE_POCSAG); @@ -1899,6 +2025,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(true); if (m_ax25Network != NULL) m_ax25Network->enable(true); if (m_dstar != NULL) @@ -1913,17 +2041,20 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(true); if (m_ax25 != NULL) m_ax25->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } + m_modem->setMode(MODE_FM); if (m_ump != NULL) m_ump->setMode(MODE_FM); m_display->setFM(); m_mode = MODE_FM; - m_modeTimer.stop(); + m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("FM"); break; @@ -1941,6 +2072,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1955,6 +2088,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -1985,6 +2120,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); if (m_ax25Network != NULL) m_ax25Network->enable(false); if (m_dstar != NULL) @@ -1999,6 +2136,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); if (m_ax25 != NULL) m_ax25->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -2027,6 +2166,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdnNetwork->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(true); if (m_ax25Network != NULL) m_ax25Network->enable(true); if (m_dstar != NULL) @@ -2041,6 +2182,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_nxdn->enable(true); if (m_pocsag != NULL) m_pocsag->enable(true); + if (m_fm != NULL) + m_fm->enable(true); if (m_ax25 != NULL) m_ax25->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -2121,38 +2264,38 @@ void CMMDVMHost::remoteControl() processModeCommand(MODE_NXDN, m_nxdnRFModeHang); break; case RCD_MODE_FM: - if (m_fmEnabled != false) + if (m_fmEnabled) processModeCommand(MODE_FM, 0); break; case RCD_ENABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==false) + if (m_dstar != NULL && !m_dstarEnabled) processEnableCommand(m_dstarEnabled, true); - if (m_dstarNetwork != NULL) - m_dstarNetwork->enable(true); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(true); break; case RCD_ENABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==false) + if (m_dmr != NULL && !m_dmrEnabled) processEnableCommand(m_dmrEnabled, true); - if (m_dmrNetwork != NULL) - m_dmrNetwork->enable(true); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(true); break; case RCD_ENABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==false) + if (m_ysf != NULL && !m_ysfEnabled) processEnableCommand(m_ysfEnabled, true); - if (m_ysfNetwork != NULL) - m_ysfNetwork->enable(true); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(true); break; case RCD_ENABLE_P25: - if (m_p25 != NULL && m_p25Enabled==false) + if (m_p25 != NULL && !m_p25Enabled) processEnableCommand(m_p25Enabled, true); - if (m_p25Network != NULL) - m_p25Network->enable(true); + if (m_p25Network != NULL) + m_p25Network->enable(true); break; case RCD_ENABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==false) + if (m_nxdn != NULL && !m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, true); - if (m_nxdnNetwork != NULL) - m_nxdnNetwork->enable(true); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(true); break; case RCD_ENABLE_FM: if (!m_fmEnabled) @@ -2163,37 +2306,37 @@ void CMMDVMHost::remoteControl() processEnableCommand(m_ax25Enabled, true); break; case RCD_DISABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==true) + if (m_dstar != NULL && m_dstarEnabled) processEnableCommand(m_dstarEnabled, false); - if (m_dstarNetwork != NULL) - m_dstarNetwork->enable(false); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); break; case RCD_DISABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==true) + if (m_dmr != NULL && m_dmrEnabled) processEnableCommand(m_dmrEnabled, false); - if (m_dmrNetwork != NULL) - m_dmrNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); break; case RCD_DISABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==true) + if (m_ysf != NULL && m_ysfEnabled) processEnableCommand(m_ysfEnabled, false); - if (m_ysfNetwork != NULL) - m_ysfNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); break; case RCD_DISABLE_P25: - if (m_p25 != NULL && m_p25Enabled==true) + if (m_p25 != NULL && m_p25Enabled) processEnableCommand(m_p25Enabled, false); - if (m_p25Network != NULL) - m_p25Network->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); break; case RCD_DISABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==true) + if (m_nxdn != NULL && m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, false); - if (m_nxdnNetwork != NULL) - m_nxdnNetwork->enable(false); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(false); break; case RCD_DISABLE_FM: - if (m_fmEnabled == true) + if (m_fmEnabled) processEnableCommand(m_fmEnabled, false); break; case RCD_DISABLE_AX25: diff --git a/MMDVMHost.h b/MMDVMHost.h index 64b1413..c44ed5f 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -35,8 +35,10 @@ #include "YSFNetwork.h" #include "P25Network.h" #include "DMRNetwork.h" +#include "FMNetwork.h" #include "DMRLookup.h" #include "MobileGPS.h" +#include "FMControl.h" #include "Display.h" #include "Timer.h" #include "Modem.h" @@ -64,6 +66,7 @@ private: CP25Control* m_p25; CNXDNControl* m_nxdn; CPOCSAGControl* m_pocsag; + CFMControl* m_fm; CAX25Control* m_ax25; CDStarNetwork* m_dstarNetwork; CDMRNetwork* m_dmrNetwork; @@ -71,6 +74,7 @@ private: CP25Network* m_p25Network; INXDNNetwork* m_nxdnNetwork; CPOCSAGNetwork* m_pocsagNetwork; + CFMNetwork* m_fmNetwork; CAX25Network* m_ax25Network; CDisplay* m_display; CUMP* m_ump; @@ -80,12 +84,14 @@ private: unsigned int m_ysfRFModeHang; unsigned int m_p25RFModeHang; unsigned int m_nxdnRFModeHang; + unsigned int m_fmRFModeHang; unsigned int m_dstarNetModeHang; unsigned int m_dmrNetModeHang; unsigned int m_ysfNetModeHang; unsigned int m_p25NetModeHang; unsigned int m_nxdnNetModeHang; unsigned int m_pocsagNetModeHang; + unsigned int m_fmNetModeHang; CTimer m_modeTimer; CTimer m_dmrTXTimer; CTimer m_cwIdTimer; @@ -119,6 +125,7 @@ private: bool createP25Network(); bool createNXDNNetwork(); bool createPOCSAGNetwork(); + bool createFMNetwork(); bool createAX25Network(); void remoteControl(); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 535e686..8c3375b 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -184,11 +184,13 @@ + + - + @@ -284,10 +286,12 @@ + + - + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index d5c8464..a144fce 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -275,9 +275,6 @@ Header Files - - Header Files - Header Files @@ -314,6 +311,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -553,9 +559,6 @@ Source Files - - Source Files - Source Files @@ -589,5 +592,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index 455828f..8b7136f 100644 --- a/Makefile +++ b/Makefile @@ -9,12 +9,12 @@ LDFLAGS = -g OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o \ - NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ - P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o \ - SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o \ - YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ + NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ + P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ + SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ + YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi b/Makefile.Pi index 3f1bd62..c4aea75 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -9,8 +9,8 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \ + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index 817c480..1c98ef2 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -10,8 +10,8 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \ + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index 2046ca1..3ade129 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -9,8 +9,8 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \ + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o IIDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index ade4a51..3de00ec 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -9,8 +9,8 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \ + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 56be139..c58b728 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -10,8 +10,8 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ - NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o \ + DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o \ + ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o \ NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \ SHA256.o StopWatch.o Sync.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ diff --git a/Modem.cpp b/Modem.cpp index daee6c3..3b412b9 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -16,7 +16,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "SerialController.h" +#if defined(__linux__) #include "I2CController.h" +#endif #include "DStarDefines.h" #include "DMRDefines.h" #include "YSFDefines.h" @@ -82,6 +85,10 @@ const unsigned char MMDVM_AX25_DATA = 0x55U; const unsigned char MMDVM_FM_PARAMS1 = 0x60U; const unsigned char MMDVM_FM_PARAMS2 = 0x61U; const unsigned char MMDVM_FM_PARAMS3 = 0x62U; +const unsigned char MMDVM_FM_PARAMS4 = 0x63U; +const unsigned char MMDVM_FM_DATA = 0x65U; +const unsigned char MMDVM_FM_CONTROL = 0x66U; +const unsigned char MMDVM_FM_EOT = 0x67U; const unsigned char MMDVM_ACK = 0x70U; const unsigned char MMDVM_NAK = 0x7FU; @@ -160,6 +167,8 @@ m_txP25Data(1000U, "Modem TX P25"), m_rxNXDNData(1000U, "Modem RX NXDN"), m_txNXDNData(1000U, "Modem TX NXDN"), m_txPOCSAGData(1000U, "Modem TX POCSAG"), +m_rxFMData(5000U, "Modem RX FM"), +m_txFMData(5000U, "Modem TX FM"), m_rxAX25Data(1000U, "Modem RX AX.25"), m_txAX25Data(1000U, "Modem TX AX.25"), m_rxTransparentData(1000U, "Modem RX Transparent"), @@ -175,6 +184,7 @@ m_ysfSpace(0U), m_p25Space(0U), m_nxdnSpace(0U), m_pocsagSpace(0U), +m_fmSpace(0U), m_ax25Space(0U), m_tx(false), m_cd(false), @@ -195,6 +205,7 @@ m_fmCallsignAtStart(true), m_fmCallsignAtEnd(true), m_fmCallsignAtLatch(true), m_fmRfAck("K"), +m_fmExtAck("N"), m_fmAckSpeed(20U), m_fmAckFrequency(1750U), m_fmAckMinTime(4U), @@ -207,11 +218,14 @@ m_fmCtcssHighThreshold(30U), m_fmCtcssLowThreshold(20U), m_fmCtcssLevel(10.0F), m_fmKerchunkTime(0U), +m_fmKerchunkTX(true), m_fmHangTime(5U), m_fmUseCOS(true), m_fmCOSInvert(false), m_fmRFAudioBoost(1U), -m_fmMaxDevLevel(90.0F) +m_fmExtAudioBoost(1U), +m_fmMaxDevLevel(90.0F), +m_fmExtEnable(false) { m_buffer = new unsigned char[BUFFER_LENGTH]; @@ -224,13 +238,15 @@ CModem::~CModem() delete[] m_buffer; } -void CModem::setSerialParams(const std::string& protocol, unsigned int address) +void CModem::setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed) { // Create the serial controller instance according the protocol specified in conf. +#if defined(__linux__) if (protocol == "i2c") - m_serial = new CI2CController(m_port, SERIAL_115200, address, true); + m_serial = new CI2CController(m_port, address); else - m_serial = new CSerialController(m_port, SERIAL_115200, true); +#endif + m_serial = new CSerialController(m_port, speed, true); } void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) @@ -363,6 +379,16 @@ bool CModem::open() m_serial = NULL; return false; } + + if (m_fmExtEnable) { + ret = setFMExtParams(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + } } m_statusTimer.start(); @@ -408,7 +434,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX D-Star Header", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxDStarData.addData(&data, 1U); data = TAG_HEADER; @@ -422,7 +448,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX D-Star Data", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxDStarData.addData(&data, 1U); data = TAG_DATA; @@ -460,7 +486,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX DMR Data 1", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxDMRData1.addData(&data, 1U); if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) @@ -477,7 +503,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX DMR Data 2", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxDMRData2.addData(&data, 1U); if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) @@ -518,7 +544,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX YSF Data", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxYSFData.addData(&data, 1U); data = TAG_DATA; @@ -544,7 +570,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX P25 Header", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxP25Data.addData(&data, 1U); data = TAG_HEADER; @@ -558,7 +584,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX P25 LDU", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxP25Data.addData(&data, 1U); data = TAG_DATA; @@ -584,7 +610,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "RX NXDN Data", m_buffer, m_length); - unsigned char data = m_length - 2U; + unsigned char data = m_length - m_offset + 1U; m_rxNXDNData.addData(&data, 1U); data = TAG_DATA; @@ -606,12 +632,54 @@ void CModem::clock(unsigned int ms) } break; + case MMDVM_FM_DATA: { + if (m_trace) + CUtils::dump(1U, "RX FM Data", m_buffer, m_length); + + unsigned int data1 = m_length - m_offset + 1U; + m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); + + unsigned char data2 = TAG_DATA; + m_rxFMData.addData(&data2, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_FM_CONTROL: { + if (m_trace) + CUtils::dump(1U, "RX FM Control", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxFMData.addData(&data, 1U); + + data = TAG_HEADER; + m_rxFMData.addData(&data, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_FM_EOT: { + if(m_trace) + CUtils::dump(1U, "RX FM End of transmission", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxFMData.addData(&data, 1U); + + data = TAG_EOT; + m_rxFMData.addData(&data, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + case MMDVM_AX25_DATA: { if (m_trace) CUtils::dump(1U, "RX AX.25 Data", m_buffer, m_length); - unsigned char data = m_length - 3U; - m_rxAX25Data.addData(&data, 1U); + unsigned int data = m_length - m_offset; + m_rxAX25Data.addData((unsigned char*)&data, sizeof(unsigned int)); m_rxAX25Data.addData(m_buffer + m_offset, m_length - m_offset); } @@ -624,6 +692,7 @@ void CModem::clock(unsigned int ms) m_p25Space = 0U; m_nxdnSpace = 0U; m_pocsagSpace = 0U; + m_fmSpace = 0U; m_ax25Space = 0U; m_mode = m_buffer[m_offset + 1U]; @@ -662,10 +731,12 @@ void CModem::clock(unsigned int ms) if (m_length > (m_offset + 9U)) m_pocsagSpace = m_buffer[m_offset + 9U]; if (m_length > (m_offset + 10U)) - m_ax25Space = m_buffer[m_offset + 10U] * 8U; + m_fmSpace = m_buffer[m_offset + 10U]; + if (m_length > (m_offset + 11U)) + m_ax25Space = m_buffer[m_offset + 11U]; m_inactivityTimer.start(); - // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[m_offset + 2U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, m_ax25Space, int(m_lockout), int(m_cd)); + // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[m_offset + 2U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, m_fmSpace, m_ax25Space, int(m_lockout), int(m_cd)); } break; @@ -869,6 +940,23 @@ void CModem::clock(unsigned int ms) m_pocsagSpace--; } + if (m_fmSpace > 1U && !m_txFMData.isEmpty()) { + unsigned char len = 0U; + m_txFMData.getData(&len, 1U); + m_txFMData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX FM Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing FM data to the MMDVM"); + + m_playoutTimer.start(); + + m_fmSpace--; + } + if (m_ax25Space > 0U && !m_txAX25Data.isEmpty()) { unsigned char len = 0U; m_txAX25Data.getData((unsigned char*)&len, sizeof(unsigned int)); @@ -993,6 +1081,20 @@ unsigned int CModem::readNXDNData(unsigned char* data) return len; } +unsigned int CModem::readFMData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxFMData.isEmpty()) + return 0U; + + unsigned int len = 0U; + m_rxFMData.getData((unsigned char*)&len, sizeof(unsigned int)); + m_rxFMData.getData(data, len); + + return len; +} + unsigned int CModem::readAX25Data(unsigned char* data) { assert(data != NULL); @@ -1000,8 +1102,8 @@ unsigned int CModem::readAX25Data(unsigned char* data) if (m_rxAX25Data.isEmpty()) return 0U; - unsigned char len = 0U; - m_rxAX25Data.getData(&len, 1U); + unsigned int len = 0U; + m_rxAX25Data.getData((unsigned char*)&len, sizeof(unsigned int)); m_rxAX25Data.getData(data, len); return len; @@ -1248,6 +1350,40 @@ bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) return true; } +unsigned int CModem::getFMSpace() const +{ + return m_txFMData.freeSpace(); +} + +bool CModem::writeFMData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[500U]; + + unsigned int len; + if (length > 252U) { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 0U; + buffer[2U] = (length + 4U) - 255U; + buffer[3U] = MMDVM_FM_DATA; + ::memcpy(buffer + 4U, data, length); + len = length + 4U; + } else { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_FM_DATA; + ::memcpy(buffer + 3U, data, length); + len = length + 3U; + } + + m_txFMData.addData((unsigned char*)&len, sizeof(unsigned int)); + m_txFMData.addData(buffer, len); + + return true; +} + bool CModem::hasAX25Space() const { unsigned int space = m_txAX25Data.freeSpace() / (AX25_MAX_FRAME_LENGTH_BYTES + 5U); @@ -2044,7 +2180,7 @@ void CModem::setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, uns m_fmAckLevel = ackLevel; } -void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel) +void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, bool kerchunkTX, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel) { m_fmTimeout = timeout; m_fmTimeoutLevel = timeoutLevel; @@ -2055,6 +2191,8 @@ void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctc m_fmCtcssLevel = ctcssLevel; m_fmKerchunkTime = kerchunkTime; + m_fmKerchunkTX = kerchunkTX; + m_fmHangTime = hangTime; m_fmUseCOS = useCOS; @@ -2064,6 +2202,13 @@ void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctc m_fmMaxDevLevel = maxDevLevel; } +void CModem::setFMExtParams(const std::string& ack, unsigned int audioBoost) +{ + m_fmExtAck = ack; + m_fmExtAudioBoost = audioBoost; + m_fmExtEnable = true; +} + bool CModem::setFMCallsignParams() { assert(m_serial != NULL); @@ -2203,6 +2348,8 @@ bool CModem::setFMMiscParams() buffer[11U] |= 0x01U; if (m_fmCOSInvert) buffer[11U] |= 0x02U; + if (m_fmKerchunkTX) + buffer[11U] |= 0x04U; buffer[12U] = m_fmRFAudioBoost; @@ -2241,6 +2388,57 @@ bool CModem::setFMMiscParams() return true; } +bool CModem::setFMExtParams() +{ + assert(m_serial != NULL); + + unsigned char buffer[80U]; + unsigned char len = 7U + m_fmExtAck.size(); + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = len; + buffer[2U] = MMDVM_FM_PARAMS4; + + buffer[3U] = m_fmExtAudioBoost; + buffer[4U] = m_fmAckSpeed; + buffer[5U] = m_fmAckFrequency / 10U; + + buffer[6U] = (unsigned char)(m_fmAckLevel * 2.55F + 0.5F); + + for (unsigned int i = 0U; i < m_fmExtAck.size(); i++) + buffer[7U + i] = m_fmExtAck.at(i); + + // CUtils::dump(1U, "Written", buffer, len); + + int ret = m_serial->write(buffer, len); + if (ret != len) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FM_PARAMS4 command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FM_PARAMS4 command from the modem"); + return false; + } + + return true; +} + void CModem::printDebug() { if (m_buffer[2U] == MMDVM_DEBUG1) { diff --git a/Modem.h b/Modem.h index a6e711e..c794477 100644 --- a/Modem.h +++ b/Modem.h @@ -19,7 +19,7 @@ #ifndef MODEM_H #define MODEM_H -#include "SerialController.h" +#include "SerialPort.h" #include "RingBuffer.h" #include "Defines.h" #include "Timer.h" @@ -45,7 +45,7 @@ public: CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool trace, bool debug); virtual ~CModem(); - virtual void setSerialParams(const std::string& protocol, unsigned int address); + virtual void setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed); virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency); virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled); virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel, float ax25TXLevel); @@ -58,7 +58,8 @@ public: virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel); - virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel); + virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, bool kerchunkTX, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel); + virtual void setFMExtParams(const std::string& ack, unsigned int audioBoost); virtual bool open(); @@ -68,6 +69,7 @@ public: virtual unsigned int readYSFData(unsigned char* data); virtual unsigned int readP25Data(unsigned char* data); virtual unsigned int readNXDNData(unsigned char* data); + virtual unsigned int readFMData(unsigned char* data); virtual unsigned int readAX25Data(unsigned char* data); virtual unsigned int readTransparentData(unsigned char* data); @@ -80,6 +82,7 @@ public: virtual bool hasP25Space() const; virtual bool hasNXDNSpace() const; virtual bool hasPOCSAGSpace() const; + virtual unsigned int getFMSpace() const; virtual bool hasAX25Space() const; virtual bool hasTX() const; @@ -96,6 +99,7 @@ public: virtual bool writeP25Data(const unsigned char* data, unsigned int length); virtual bool writeNXDNData(const unsigned char* data, unsigned int length); virtual bool writePOCSAGData(const unsigned char* data, unsigned int length); + virtual bool writeFMData(const unsigned char* data, unsigned int length); virtual bool writeAX25Data(const unsigned char* data, unsigned int length); virtual bool writeTransparentData(const unsigned char* data, unsigned int length); @@ -166,7 +170,7 @@ private: bool m_ax25Enabled; int m_rxDCOffset; int m_txDCOffset; - CSerialController* m_serial; + ISerialPort* m_serial; unsigned char* m_buffer; unsigned int m_length; unsigned int m_offset; @@ -185,6 +189,8 @@ private: CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; CRingBuffer m_txPOCSAGData; + CRingBuffer m_rxFMData; + CRingBuffer m_txFMData; CRingBuffer m_rxAX25Data; CRingBuffer m_txAX25Data; CRingBuffer m_rxTransparentData; @@ -200,6 +206,7 @@ private: unsigned int m_p25Space; unsigned int m_nxdnSpace; unsigned int m_pocsagSpace; + unsigned int m_fmSpace; unsigned int m_ax25Space; bool m_tx; bool m_cd; @@ -221,6 +228,7 @@ private: bool m_fmCallsignAtEnd; bool m_fmCallsignAtLatch; std::string m_fmRfAck; + std::string m_fmExtAck; unsigned int m_fmAckSpeed; unsigned int m_fmAckFrequency; unsigned int m_fmAckMinTime; @@ -233,11 +241,14 @@ private: unsigned int m_fmCtcssLowThreshold; float m_fmCtcssLevel; unsigned int m_fmKerchunkTime; + bool m_fmKerchunkTX; unsigned int m_fmHangTime; bool m_fmUseCOS; bool m_fmCOSInvert; unsigned int m_fmRFAudioBoost; + unsigned int m_fmExtAudioBoost; float m_fmMaxDevLevel; + bool m_fmExtEnable; bool readVersion(); bool readStatus(); @@ -246,6 +257,7 @@ private: bool setFMCallsignParams(); bool setFMAckParams(); bool setFMMiscParams(); + bool setFMExtParams(); void printDebug(); diff --git a/SerialController.cpp b/SerialController.cpp index cfb448d..970501a 100644 --- a/SerialController.cpp +++ b/SerialController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -23,12 +23,11 @@ #include #include -#include - #if defined(_WIN32) || defined(_WIN64) #include #include #else +#include #include #include #include @@ -40,7 +39,7 @@ #if defined(_WIN32) || defined(_WIN64) -CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +CSerialController::CSerialController(const std::string& device, unsigned int speed, bool assertRTS) : m_device(device), m_speed(speed), m_assertRTS(assertRTS), @@ -221,7 +220,7 @@ void CSerialController::close() #else -CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +CSerialController::CSerialController(const std::string& device, unsigned int speed, bool assertRTS) : m_device(device), m_speed(speed), m_assertRTS(assertRTS), @@ -273,40 +272,44 @@ bool CSerialController::open() #endif switch (m_speed) { - case SERIAL_1200: + case 1200U: ::cfsetospeed(&termios, B1200); ::cfsetispeed(&termios, B1200); break; - case SERIAL_2400: + case 2400U: ::cfsetospeed(&termios, B2400); ::cfsetispeed(&termios, B2400); break; - case SERIAL_4800: + case 4800U: ::cfsetospeed(&termios, B4800); ::cfsetispeed(&termios, B4800); break; - case SERIAL_9600: + case 9600U: ::cfsetospeed(&termios, B9600); ::cfsetispeed(&termios, B9600); break; - case SERIAL_19200: + case 19200U: ::cfsetospeed(&termios, B19200); ::cfsetispeed(&termios, B19200); break; - case SERIAL_38400: + case 38400U: ::cfsetospeed(&termios, B38400); ::cfsetispeed(&termios, B38400); break; - case SERIAL_115200: + case 115200U: ::cfsetospeed(&termios, B115200); ::cfsetispeed(&termios, B115200); break; - case SERIAL_230400: + case 230400U: ::cfsetospeed(&termios, B230400); ::cfsetispeed(&termios, B230400); break; + case 460800U: + ::cfsetospeed(&termios, B460800); + ::cfsetispeed(&termios, B460800); + break; default: - LogError("Unsupported serial port speed - %d", int(m_speed)); + LogError("Unsupported serial port speed - %u", m_speed); ::close(m_fd); return false; } diff --git a/SerialController.h b/SerialController.h index 6dfc461..c7f97eb 100644 --- a/SerialController.h +++ b/SerialController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -28,21 +28,9 @@ #include #endif -enum SERIAL_SPEED { - SERIAL_1200 = 1200, - SERIAL_2400 = 2400, - SERIAL_4800 = 4800, - SERIAL_9600 = 9600, - SERIAL_19200 = 19200, - SERIAL_38400 = 38400, - SERIAL_76800 = 76800, - SERIAL_115200 = 115200, - SERIAL_230400 = 230400 -}; - class CSerialController : public ISerialPort { public: - CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false); + CSerialController(const std::string& device, unsigned int speed, bool assertRTS = false); virtual ~CSerialController(); virtual bool open(); @@ -59,7 +47,7 @@ public: protected: std::string m_device; - SERIAL_SPEED m_speed; + unsigned int m_speed; bool m_assertRTS; #if defined(_WIN32) || defined(_WIN64) HANDLE m_handle; diff --git a/Tools/DeEmphasis.py b/Tools/DeEmphasis.py new file mode 100644 index 0000000..44320c9 --- /dev/null +++ b/Tools/DeEmphasis.py @@ -0,0 +1,43 @@ +#based on https://github.com/gnuradio/gnuradio/blob/master/gr-analog/python/analog/fm_emph.py + +import math +import cmath +import numpy as np +import scipy.signal as signal +import pylab as pl + +tau = 750e-6 +fs = 8000 +fh = 2700 + +# Digital corner frequency +w_c = 1.0 / tau + +# Prewarped analog corner frequency +w_ca = 2.0 * fs * math.tan(w_c / (2.0 * fs)) + +# Resulting digital pole, zero, and gain term from the bilinear +# transformation of H(s) = w_ca / (s + w_ca) to +# H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) +k = -w_ca / (2.0 * fs) +z1 = -1.0 +p1 = (1.0 + k) / (1.0 - k) +b0 = -k / (1.0 - k) + +btaps = [ b0 * 1.0, b0 * -z1, 0 ] +ataps = [ 1.0, -p1, 0 ] + +# Since H(s = 0) = 1.0, then H(z = 1) = 1.0 and has 0 dB gain at DC + + +taps = np.concatenate((btaps, ataps), axis=0) +print("Taps") +print(*taps, "", sep=",", end="\n") + +f,h = signal.freqz(btaps,ataps, fs=fs) +pl.plot(f, 20*np.log10(np.abs(h))) +pl.xlabel('frequency/Hz') +pl.ylabel('gain/dB') +pl.ylim(top=0,bottom=-30) +pl.xlim(left=0, right=fh*2.5) +pl.show() \ No newline at end of file diff --git a/Tools/PreEmphasis.py b/Tools/PreEmphasis.py new file mode 100644 index 0000000..22c81f6 --- /dev/null +++ b/Tools/PreEmphasis.py @@ -0,0 +1,51 @@ +#based on https://github.com/gnuradio/gnuradio/blob/master/gr-analog/python/analog/fm_emph.py + +import math +import cmath +import numpy as np +import scipy.signal as signal +import pylab as pl + +tau = 750e-6 +fs = 8000 +fh = 2700 + +# Digital corner frequencies +w_cl = 1.0 / tau +w_ch = 2.0 * math.pi * fh + +# Prewarped analog corner frequencies +w_cla = 2.0 * fs * math.tan(w_cl / (2.0 * fs)) +w_cha = 2.0 * fs * math.tan(w_ch / (2.0 * fs)) + +# Resulting digital pole, zero, and gain term from the bilinear +# transformation of H(s) = (s + w_cla) / (s + w_cha) to +# H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) +kl = -w_cla / (2.0 * fs) +kh = -w_cha / (2.0 * fs) +z1 = (1.0 + kl) / (1.0 - kl) +p1 = (1.0 + kh) / (1.0 - kh) +b0 = (1.0 - kl) / (1.0 - kh) + +# Since H(s = infinity) = 1.0, then H(z = -1) = 1.0 and +# this filter has 0 dB gain at fs/2.0. +# That isn't what users are going to expect, so adjust with a +# gain, g, so that H(z = 1) = 1.0 for 0 dB gain at DC. +w_0dB = 2.0 * math.pi * 0.0 +g = abs(1.0 - p1 * cmath.rect(1.0, -w_0dB)) \ +/ (b0 * abs(1.0 - z1 * cmath.rect(1.0, -w_0dB))) + +btaps = [ g * b0 * 1.0, g * b0 * -z1, 0] +ataps = [ 1.0, -p1, 0] + +taps = np.concatenate((btaps, ataps), axis=0) +print("Taps") +print(*taps, "", sep=",", end="\n") + +f,h = signal.freqz(btaps,ataps, fs=fs) +pl.plot(f, 20*np.log10(np.abs(h))) +pl.xlabel('frequency/Hz') +pl.ylabel('gain/dB') +pl.ylim(top=30,bottom=0) +pl.xlim(left=0, right=fh*2.5) +pl.show() \ No newline at end of file diff --git a/UMP.cpp b/UMP.cpp index f843322..4aca606 100644 --- a/UMP.cpp +++ b/UMP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ const unsigned char UMP_STATUS = 0x50U; const unsigned int BUFFER_LENGTH = 255U; CUMP::CUMP(const std::string& port) : -m_serial(port, SERIAL_115200), +m_serial(port, 115200U), m_open(false), m_buffer(NULL), m_length(0U), diff --git a/Version.h b/Version.h index 9bb9c4a..4559871 100644 --- a/Version.h +++ b/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20200624"; +const char* VERSION = "20200625"; #endif