diff --git a/Conf.cpp b/Conf.cpp index 851dbe7..3b1ef41 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -92,6 +92,7 @@ m_modemYSFTXLevel(50U), m_modemP25TXLevel(50U), m_modemOscOffset(0), m_modemRSSIMappingFile(), +m_modemSamplesDir(), m_modemDebug(false), m_umpEnabled(false), m_umpPort(), @@ -99,6 +100,7 @@ m_dstarEnabled(false), m_dstarModule("C"), m_dstarSelfOnly(false), m_dstarBlackList(), +m_dstarErrorReply(true), m_dmrEnabled(false), m_dmrBeacons(false), m_dmrId(0U), @@ -330,6 +332,8 @@ bool CConf::read() m_modemOscOffset = ::atoi(value); else if (::strcmp(key, "RSSIMappingFile") == 0) m_modemRSSIMappingFile = value; + else if (::strcmp(key, "SamplesDir") == 0) + m_modemSamplesDir = value; else if (::strcmp(key, "Debug") == 0) m_modemDebug = ::atoi(value) == 1; } else if (section == SECTION_UMP) { @@ -359,7 +363,8 @@ bool CConf::read() } p = ::strtok(NULL, ",\r\n"); } - } + } else if (::strcmp(key, "ErrorReply") == 0) + m_dstarErrorReply = ::atoi(value) == 1; } else if (section == SECTION_DMR) { if (::strcmp(key, "Enable") == 0) m_dmrEnabled = ::atoi(value) == 1; @@ -744,6 +749,11 @@ std::string CConf::getModemRSSIMappingFile () const return m_modemRSSIMappingFile; } +std::string CConf::getModemSamplesDir() const +{ + return m_modemSamplesDir; +} + bool CConf::getModemDebug() const { return m_modemDebug; @@ -779,6 +789,11 @@ std::vector CConf::getDStarBlackList() const return m_dstarBlackList; } +bool CConf::getDStarErrorReply() const +{ + return m_dstarErrorReply; +} + bool CConf::getDMREnabled() const { return m_dmrEnabled; diff --git a/Conf.h b/Conf.h index 8aca5b0..4983643 100644 --- a/Conf.h +++ b/Conf.h @@ -79,6 +79,7 @@ public: unsigned int getModemP25TXLevel() const; int getModemOscOffset() const; std::string getModemRSSIMappingFile() const; + std::string getModemSamplesDir() const; bool getModemDebug() const; // The UMP section @@ -90,6 +91,7 @@ public: std::string getDStarModule() const; bool getDStarSelfOnly() const; std::vector getDStarBlackList() const; + bool getDStarErrorReply() const; // The DMR section bool getDMREnabled() const; @@ -229,6 +231,7 @@ private: unsigned int m_modemP25TXLevel; int m_modemOscOffset; std::string m_modemRSSIMappingFile; + std::string m_modemSamplesDir; bool m_modemDebug; bool m_umpEnabled; @@ -238,6 +241,7 @@ private: std::string m_dstarModule; bool m_dstarSelfOnly; std::vector m_dstarBlackList; + bool m_dstarErrorReply; bool m_dmrEnabled; bool m_dmrBeacons; diff --git a/DStarControl.cpp b/DStarControl.cpp index f8d4e9c..48713c1 100644 --- a/DStarControl.cpp +++ b/DStarControl.cpp @@ -36,10 +36,11 @@ bool CallsignCompare(const std::string& arg, const unsigned char* my) // #define DUMP_DSTAR -CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) : +CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool errorReply, const std::vector& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) : m_callsign(NULL), m_gateway(NULL), m_selfOnly(selfOnly), +m_errorReply(errorReply), m_blackList(blackList), m_network(network), m_display(display), @@ -58,6 +59,7 @@ m_rfTimeoutTimer(1000U, timeout), m_netTimeoutTimer(1000U, timeout), m_packetTimer(1000U, 0U, 300U), m_ackTimer(1000U, 0U, 750U), +m_errTimer(1000U, 0U, 750U), m_interval(), m_elapsed(), m_rfFrames(0U), @@ -126,6 +128,20 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) return false; } + if (type == TAG_LOST && m_rfState == RS_RF_INVALID) { + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + if (m_errorReply) + m_errTimer.start(); + + if (m_network != NULL) + m_network->reset(); + } + + return false; + } + if (type == TAG_LOST) { m_rfState = RS_RF_LISTENING; return false; @@ -177,6 +193,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) if (type == TAG_HEADER) { CDStarHeader header(data + 1U); + m_rfHeader = header; unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; header.getMyCall1(my1); @@ -184,7 +201,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) // Is this a transmission destined for a repeater? if (!header.isRepeater()) { LogMessage("D-Star, non repeater RF header received from %8.8s", my1); - m_rfState = RS_RF_REJECTED; + m_rfState = RS_RF_INVALID; return false; } @@ -194,7 +211,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) // Is it for us? if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); - m_rfState = RS_RF_REJECTED; + m_rfState = RS_RF_INVALID; return false; } @@ -225,9 +242,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) if (!m_rfTimeoutTimer.isRunning()) m_rfTimeoutTimer.start(); - m_rfHeader = header; - m_ackTimer.stop(); + m_errTimer.stop(); m_rfBits = 1U; m_rfErrs = 0U; @@ -271,6 +287,18 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else if (type == TAG_EOT) { if (m_rfState == RS_RF_REJECTED) { m_rfState = RS_RF_LISTENING; + } else if (m_rfState == RS_RF_INVALID) { + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + if (m_errorReply) + m_errTimer.start(); + + if (m_network != NULL) + m_network->reset(); + } + + return false; } else if (m_rfState == RS_RF_AUDIO) { if (m_net) writeNetworkDataRF(DSTAR_END_PATTERN_BYTES, 0U, true); @@ -290,6 +318,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else if (type == TAG_DATA) { if (m_rfState == RS_RF_REJECTED) { return false; + } else if (m_rfState == RS_RF_INVALID) { + return false; } else if (m_rfState == RS_RF_LISTENING) { // The sync is regenerated by the modem so can do exact match if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { @@ -340,8 +370,15 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) if (header == NULL) return false; + m_rfHeader = *header; + + unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; + header->getMyCall1(my1); + // Is this a transmission destined for a repeater? if (!header->isRepeater()) { + LogMessage("D-Star, non repeater RF header received from %8.8s", my1); + m_rfState = RS_RF_INVALID; delete header; return false; } @@ -351,19 +388,22 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) // Is it for us? if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { + LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); + m_rfState = RS_RF_INVALID; delete header; return false; } - unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; - header->getMyCall1(my1); - if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; delete header; return false; } if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; delete header; return false; } @@ -385,8 +425,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) // Create a dummy start frame to replace the received frame m_ackTimer.stop(); - - m_rfHeader = *header; + m_errTimer.stop(); m_rfBits = 1U; m_rfErrs = 0U; @@ -550,6 +589,7 @@ void CDStarControl::writeNetwork() m_packetTimer.start(); //m_elapsed.start(); // commented out and placed lower down due to delay introduced somewhere below here. m_ackTimer.stop(); + m_errTimer.stop(); m_lastFrameValid = false; @@ -655,6 +695,12 @@ void CDStarControl::clock() m_ackTimer.stop(); } + m_errTimer.clock(ms); + if (m_errTimer.isRunning() && m_errTimer.hasExpired()) { + sendError(); + m_errTimer.stop(); + } + m_rfTimeoutTimer.clock(ms); m_netTimeoutTimer.clock(ms); @@ -1004,3 +1050,45 @@ void CDStarControl::sendAck() writeQueueEOTRF(); } + +void CDStarControl::sendError() +{ + unsigned char user[DSTAR_LONG_CALLSIGN_LENGTH]; + m_rfHeader.getMyCall1(user); + + CDStarHeader header; + header.setUnavailable(true); + header.setMyCall1(m_callsign); + header.setYourCall(user); + header.setRPTCall1(m_callsign); + header.setRPTCall2(m_callsign); + + unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 1U]; + header.get(data + 1U); + data[0U] = TAG_HEADER; + + writeQueueHeaderRF(data); + + writeQueueDataRF(DSTAR_NULL_FRAME_SYNC_BYTES); + + LINK_STATUS status = LS_NONE; + unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH]; + if (m_network != NULL) + m_network->getStatus(status, reflector); + + char text[40U]; + if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_LINKED_LOOPBACK) + ::sprintf(text, "%-8.8s BER: %.1f%% ", reflector, float(m_rfErrs * 100U) / float(m_rfBits)); + else + ::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits)); + m_slowData.setText(text); + + ::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U); + + for (unsigned int i = 0U; i < 19U; i++) { + m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); + writeQueueDataRF(data); + } + + writeQueueEOTRF(); +} diff --git a/DStarControl.h b/DStarControl.h index 9eab5e3..84b4f3d 100644 --- a/DStarControl.h +++ b/DStarControl.h @@ -37,7 +37,7 @@ class CDStarControl { public: - CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper); + CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool errorReply, const std::vector& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper); ~CDStarControl(); bool writeModem(unsigned char* data, unsigned int len); @@ -50,6 +50,7 @@ private: unsigned char* m_callsign; unsigned char* m_gateway; bool m_selfOnly; + bool m_errorReply; std::vector m_blackList; CDStarNetwork* m_network; CDisplay* m_display; @@ -68,6 +69,7 @@ private: CTimer m_netTimeoutTimer; CTimer m_packetTimer; CTimer m_ackTimer; + CTimer m_errTimer; CStopWatch m_interval; CStopWatch m_elapsed; unsigned int m_rfFrames; @@ -112,6 +114,7 @@ private: void blankDTMF(unsigned char* data) const; void sendAck(); + void sendError(); }; #endif diff --git a/Defines.h b/Defines.h index 6b81e7a..22baf2d 100644 --- a/Defines.h +++ b/Defines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017 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 @@ -44,7 +44,8 @@ enum RPT_RF_STATE { RS_RF_LATE_ENTRY, RS_RF_AUDIO, RS_RF_DATA, - RS_RF_REJECTED + RS_RF_REJECTED, + RS_RF_INVALID }; enum RPT_NET_STATE { diff --git a/MMDVM.ini b/MMDVM.ini index cacc2ff..6332db6 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -51,6 +51,7 @@ TXLevel=50 # P25TXLevel=50 OscOffset=0 RSSIMappingFile=RSSI.dat +SamplesDir=. Debug=0 [UMP] @@ -62,6 +63,7 @@ Port=/dev/ttyACM1 Enable=1 Module=C SelfOnly=0 +ErrorReply=1 [DMR] Enable=1 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 22e9707..a648007 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -329,15 +329,17 @@ int CMMDVMHost::run() std::string module = m_conf.getDStarModule(); bool selfOnly = m_conf.getDStarSelfOnly(); std::vector blackList = m_conf.getDStarBlackList(); + bool errorReply = m_conf.getDStarErrorReply(); LogInfo("D-Star Parameters"); LogInfo(" Module: %s", module.c_str()); LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" Error Reply: %s", errorReply ? "yes" : "no"); if (blackList.size() > 0U) LogInfo(" Black List: %u", blackList.size()); - dstar = new CDStarControl(m_callsign, module, selfOnly, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex, rssi); + dstar = new CDStarControl(m_callsign, module, selfOnly, errorReply, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex, rssi); } CDMRControl* dmr = NULL; @@ -793,6 +795,7 @@ bool CMMDVMHost::createModem() unsigned int rxFrequency = m_conf.getRxFrequency(); unsigned int txFrequency = m_conf.getTxFrequency(); int oscOffset = m_conf.getModemOscOffset(); + std::string samplesDir = m_conf.getModemSamplesDir(); LogInfo("Modem Parameters"); LogInfo(" Port: %s", port.c_str()); @@ -809,10 +812,9 @@ bool CMMDVMHost::createModem() LogInfo(" P25 TX Level: %u%%", p25TXLevel); LogInfo(" RX Frequency: %uHz", rxFrequency); LogInfo(" TX Frequency: %uHz", txFrequency); - LogInfo(" Osc. Offset: %dppm", oscOffset); - m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, oscOffset, debug); + m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, oscOffset, samplesDir, debug); m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel); m_modem->setRFParams(rxFrequency, txFrequency); diff --git a/Modem.cpp b/Modem.cpp index d4429e0..5565b38 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #if defined(_WIN32) || defined(_WIN64) #include @@ -81,10 +82,10 @@ const unsigned char MMDVM_DEBUG5 = 0xF5U; const unsigned int MAX_RESPONSES = 30U; -const unsigned int BUFFER_LENGTH = 500U; +const unsigned int BUFFER_LENGTH = 2000U; -CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, bool debug) : +CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, const std::string& samplesDir, bool debug) : m_port(port), m_colorCode(0U), m_duplex(duplex), @@ -100,6 +101,7 @@ m_dmrTXLevel(0U), m_ysfTXLevel(0U), m_p25TXLevel(0U), m_oscOffset(oscOffset), +m_samplesDir(samplesDir), m_debug(debug), m_rxFrequency(0U), m_txFrequency(0U), @@ -474,7 +476,7 @@ void CModem::clock(unsigned int ms) break; case MMDVM_SAMPLES: - printSamples(); + dumpSamples(); break; default: @@ -1128,6 +1130,22 @@ RESP_TYPE_MMDVM CModem::getResponse() } if (m_offset >= 3U) { + // Use later two byte length field + if (m_length == 0U) { + int ret = m_serial.read(m_buffer + 3U, 2U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_offset = 0U; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_length = (m_buffer[3U] << 8) | m_buffer[4U]; + m_offset = 5U; + } + while (m_offset < m_length) { int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset); if (ret < 0) { @@ -1277,39 +1295,49 @@ void CModem::printDebug() } } -void CModem::printSamples() +void CModem::dumpSamples() { + if (m_samplesDir.empty()) + m_samplesDir = "."; + + time_t now; + ::time(&now); + + struct tm* tm = ::localtime(&now); + const char* mode = NULL; - switch (m_buffer[3U]) { + switch (m_buffer[5U]) { case MODE_DSTAR: - mode = "D-Star"; + mode = "DStar"; break; case MODE_DMR: mode = "DMR"; break; - case MODE_YSF: - mode = "YSF"; - break; case MODE_P25: mode = "P25"; break; - default: - mode = "???"; + case MODE_YSF: + mode = "YSF"; break; + default: + LogWarning("Unknown protocol passed to samples dump - %u", m_buffer[5U]); + return; } - char samples[250U]; - samples[0U] = '\0'; + char filename[150U]; +#if defined(_WIN32) || defined(_WIN64) + ::sprintf(filename, "%s\\Samples-%s-%04d%02d%02d.dat", m_samplesDir.c_str(), mode, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/Samples-%s-%04d%02d%02d.dat", m_samplesDir.c_str(), mode, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif - unsigned char n = (m_buffer[1U] - 4U) / 2U; - - for (unsigned char i = 0U; i < n; i++) { - unsigned char index = i * 2U + 4U; - - short val = (m_buffer[index + 0U] << 8) | m_buffer[index + 1U]; - - ::sprintf(samples + ::strlen(samples), " %d", val - 2048); + FILE* fp = ::fopen(filename, "a+b"); + if (fp == NULL) { + LogWarning("Unable to open samples file for writing - %s", filename); + return; } - LogMessage("Debug: Samples dump: %s:%s", mode, samples); + ::fwrite(m_buffer + 6U, 1U, m_length, fp); + + ::fclose(fp); } diff --git a/Modem.h b/Modem.h index a4d2afd..6c31992 100644 --- a/Modem.h +++ b/Modem.h @@ -34,7 +34,7 @@ enum RESP_TYPE_MMDVM { class CModem { public: - CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, bool debug = false); + CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, const std::string& samplesDir, bool debug = false); ~CModem(); void setRFParams(unsigned int rxFrequency, unsigned int txFrequency); @@ -102,6 +102,7 @@ private: unsigned int m_ysfTXLevel; unsigned int m_p25TXLevel; int m_oscOffset; + std::string m_samplesDir; bool m_debug; unsigned int m_rxFrequency; unsigned int m_txFrequency; @@ -143,7 +144,7 @@ private: bool setFrequency(); void printDebug(); - void printSamples(); + void dumpSamples(); RESP_TYPE_MMDVM getResponse(); }; diff --git a/P25Data.cpp b/P25Data.cpp index 06d25c6..6f787ce 100644 --- a/P25Data.cpp +++ b/P25Data.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017 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 @@ -96,9 +96,14 @@ bool CP25Data::decodeLDU1(const unsigned char* data) CP25Utils::decode(data, raw, 1356U, 1398U); decodeLDUHamming(raw, rs + 15U); - bool ret = m_rs241213.decode(rs); - if (!ret) + try { + bool ret = m_rs241213.decode(rs); + if (!ret) + return false; + } catch (...) { + CUtils::dump(2U, "P25, RS carshed with input data", rs, 18U); return false; + } // Simple validation of the source id unsigned int srcId = (rs[6U] << 16) + (rs[7U] << 8) + rs[8U];