From 8946038e0e619f7b3bdf7aa146d8667a77873199 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 17 Oct 2020 16:25:48 +0100 Subject: [PATCH] Add M17 reflector linking and start on the frame handling. --- M17CRC.h | 34 ++++ M17Control.cpp | 371 ++++++++++++++++++++++++++------------ M17Convolution.h | 44 +++++ M17Defines.h | 22 ++- M17LICH.h | 6 + M17Network.cpp | 177 ++++++++++++++++-- M17Network.h | 23 ++- M17Utils.h | 36 ++++ MMDVMHost.vcxproj | 3 + MMDVMHost.vcxproj.filters | 9 + 10 files changed, 594 insertions(+), 131 deletions(-) create mode 100644 M17CRC.h create mode 100644 M17Convolution.h create mode 100644 M17Utils.h diff --git a/M17CRC.h b/M17CRC.h new file mode 100644 index 0000000..af0f3d0 --- /dev/null +++ b/M17CRC.h @@ -0,0 +1,34 @@ +/* + * 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(M17CRC_H) +#define M17CRC_H + +#include + +class CM17CRC +{ +public: + static bool checkCRC(const unsigned char* in, unsigned int length); + static void encodeCRC(unsigned char* in, unsigned int length); + +private: + static uint16_t createCRC(const unsigned char* in, unsigned int length); +}; + +#endif diff --git a/M17Control.cpp b/M17Control.cpp index fbf9b1e..24cc8c3 100644 --- a/M17Control.cpp +++ b/M17Control.cpp @@ -12,6 +12,9 @@ */ #include "M17Control.h" +#include "M17Convolution.h" +#include "M17CRC.h" +#include "Golay24128.h" #include "Utils.h" #include "Sync.h" #include "Log.h" @@ -91,7 +94,7 @@ CM17Control::~CM17Control() { } -bool CM17Control::writeModem(unsigned char *data, unsigned int len) +bool CM17Control::writeModem(unsigned char* data, unsigned int len) { assert(data != NULL); @@ -102,7 +105,7 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { std::string source = m_rfLICH.getSource(); - std::string dest = m_rfLICH.getDest(); + std::string dest = m_rfLICH.getDest(); if (m_rssi != 0U) LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); @@ -119,8 +122,7 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) if (type == TAG_LOST) { m_rfState = RS_RF_LISTENING; - m_rfMask = 0x00U; - m_rfLayer3.reset(); + m_rfMask = 0x00U; return false; } @@ -151,98 +153,193 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) decorrelator(data + 2U, temp); interleaver(temp, data + 2U); - CM17LICH lich; - bool valid = lich.decode(data + 2U); + if (m_rfState == RS_RF_LISTENING) { + m_rfMask = 0x00U; - if (valid) - m_rfLastLICH = lich; + CM17Convolution conv; + conv.start(); - unsigned char usc = m_rfLastLICH.getFCT(); - unsigned char option = m_rfLastLICH.getOption(); + unsigned int n = 2U + M17_SYNC_LENGTH_BYTES; + for (unsigned int i = 0U; i < (M17_LICH_LENGTH_BYTES / 2U); i++) { + uint8_t s0 = data[n++]; + uint8_t s1 = data[n++]; - CM17SACCH sacch; - bool valid = sacch.decode(data + 2U); - if (valid) { - unsigned char ran = sacch.getRAN(); - if (ran != m_ran && ran != 0U) - return false; - } else if (m_rfState == RS_RF_LISTENING) { - return false; - } - - unsigned char netData[40U]; - ::memset(netData, 0x00U, 40U); - - if (usc == M17_LICH_USC_SACCH_NS) { - // The SACCH on a non-superblock frame is usually an idle and not interesting apart from the RAN. - CM17FACCH1 facch; - bool valid = facch.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS); - if (!valid) - valid = facch.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS + M17_FACCH1_LENGTH_BITS); - if (!valid) - return false; - - unsigned char buffer[10U]; - facch.getData(buffer); - - CM17Layer3 layer3; - layer3.decode(buffer, M17_FACCH1_LENGTH_BITS); - - unsigned char type = layer3.getMessageType(); - if (type == M17_MESSAGE_TYPE_TX_REL) { - if (m_rfState != RS_RF_AUDIO) { - m_rfState = RS_RF_LISTENING; - m_rfMask = 0x00U; - m_rfLayer3.reset(); - return false; - } - } else if (type == M17_MESSAGE_TYPE_VCALL) { - if (m_rfState == RS_RF_LISTENING && m_selfOnly) { - unsigned short srcId = layer3.getSourceUnitId(); - if (srcId != m_id) { - m_rfState = RS_RF_REJECTED; - return false; - } - } - } else { - return false; + conv.decode(s0, s1); } - m_rfLayer3 = layer3; + unsigned char frame[M17_LICH_LENGTH_BYTES]; + conv.chainback(frame, M17_LICH_LENGTH_BITS); - data[0U] = type == M17_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA; - data[1U] = 0x00U; + bool valid = CM17CRC::checkCRC(frame, M17_LICH_LENGTH_BITS); + if (valid) { + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; - CSync::addM17Sync(data + 2U); +#if defined(DUMP_M17) + openFile(); +#endif + m_rfLICH.setLinkSetup(frame); - CM17LICH lich; - lich.setRFCT(M17_LICH_RFCT_RDCH); - lich.setFCT(M17_LICH_USC_SACCH_NS); - lich.setOption(M17_LICH_STEAL_FACCH); - lich.setDirection(m_remoteGateway || !m_duplex ? M17_LICH_DIRECTION_INBOUND : M17_LICH_DIRECTION_OUTBOUND); - lich.encode(data + 2U); + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); - lich.setDirection(M17_LICH_DIRECTION_INBOUND); - netData[0U] = lich.getRaw(); + unsigned char dataType = m_rfLICH.getDataType(); + switch (dataType) { + case 1U: + LogMessage("M17, received RF data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + case 2U: + LogMessage("M17, received RF voice transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + case 3U: + LogMessage("M17, received RF voice + data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + default: + LogMessage("M17, received RF unknown transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + } - CM17SACCH sacch; - sacch.setRAN(m_ran); - sacch.setStructure(M17_SR_SINGLE); - sacch.setData(SACCH_IDLE); - sacch.encode(data + 2U); + m_display->writeM17(source.c_str(), dest.c_str(), "R"); - sacch.getRaw(netData + 1U); +#if defined(DUMP_M17) + writeFile(data + 2U); +#endif + if (m_duplex) { + data[0U] = TAG_DATA; + data[1U] = 0x00U; - facch.encode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS); - facch.encode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS + M17_FACCH1_LENGTH_BITS); + // Generate the sync + CSync::addM17Sync(data + 2U); - facch.getRaw(netData + 5U + 0U); - facch.getRaw(netData + 5U + 14U); + unsigned char setup[M17_LICH_LENGTH_BYTES]; + m_rfLICH.getLinkSetup(data + 2U); - scrambler(data + 2U); + // Add the CRC + CM17CRC::encodeCRC(data + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); - writeNetwork(netData, data[0U] == TAG_EOT ? NNMT_VOICE_TRAILER : NNMT_VOICE_HEADER); + // Add the convolution FEC + CM17Convolution conv; + conv.encode(setup, data + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); + unsigned char temp[M17_FRAME_LENGTH_BYTES]; + interleaver(data + 2U, temp); + decorrelator(temp, data + 2U); + + writeQueueRF(data); + } + } else { + m_rfState = RS_RF_LATE_ENTRY; + } + } + + if (m_rfState == RS_RF_LATE_ENTRY) { + CM17Convolution conv; + conv.start(); + + unsigned int n = 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_LENGTH_BYTES; + for (unsigned int i = 0U; i < (M17_LICH_LENGTH_BYTES / 2U); i++) { + uint8_t s0 = data[n++]; + uint8_t s1 = data[n++]; + + conv.decode(s0, s1); + } + + unsigned char frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + conv.chainback(frame, M17_FN_LENGTH_BITS + M17_PAYLOAD_LENGTH_BITS + M17_CRC_LENGTH_BITS); + + bool valid = CM17CRC::checkCRC(frame, M17_FN_LENGTH_BITS + M17_PAYLOAD_LENGTH_BITS + M17_CRC_LENGTH_BITS); + if (valid) { + unsigned int fn = (frame[0U] << 8) + (frame[1U] << 0); + + unsigned int frag1, frag2, frag3, frag4; + + // XXX TODO populate frag1-4 + + unsigned int lich1 = CGolay24128::decode24128(frag1); + unsigned int lich2 = CGolay24128::decode24128(frag2); + unsigned int lich3 = CGolay24128::decode24128(frag3); + unsigned int lich4 = CGolay24128::decode24128(frag4); + + m_rfLICH.setFragment(data + 2U + M17_SYNC_LENGTH_BYTES, fn & 0x7FFFU); + + valid = m_rfLICH.isValid(); + if (valid) { + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; + +#if defined(DUMP_M17) + openFile(); +#endif + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); + + unsigned char dataType = m_rfLICH.getDataType(); + switch (dataType) { + case 1U: + LogMessage("M17, received RF late entry data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + case 2U: + LogMessage("M17, received RF late entry voice transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + case 3U: + LogMessage("M17, received RF late entry voice + data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + default: + LogMessage("M17, received RF late entry unknown transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + } + + m_display->writeM17(source.c_str(), dest.c_str(), "R"); + + if (m_duplex) { + // Create a Link Setup frame + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + // Generate the sync + CSync::addM17Sync(data + 2U); + + unsigned char setup[M17_LICH_LENGTH_BYTES]; + m_rfLICH.getLinkSetup(data + 2U); + + // Add the CRC + CM17CRC::encodeCRC(data + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); + + // Add the convolution FEC + CM17Convolution conv; + conv.encode(setup, data + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); + + unsigned char temp[M17_FRAME_LENGTH_BYTES]; + interleaver(data + 2U, temp); + decorrelator(temp, data + 2U); + + writeQueueRF(data); + } + } + } + } + + if (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA) { #if defined(DUMP_M17) writeFile(data + 2U); #endif @@ -250,32 +347,32 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) writeQueueRF(data); if (data[0U] == TAG_EOT) { - unsigned short dstId = m_rfLayer3.getDestinationGroupId(); - bool grp = m_rfLayer3.getIsGroup(); - std::string source = m_lookup->find(m_rfLayer3.getSourceUnitId()); + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); m_rfFrames++; if (m_rssi != 0U) - LogMessage("M17, received RF end of transmission from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); else - LogMessage("M17, received RF end of transmission from %s to %s%u, %.1f seconds, BER: %.1f%%", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); writeEndRF(); - } else { - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; + } + else { + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; m_rfTimeoutTimer.start(); - m_rfState = RS_RF_AUDIO; - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; + m_rfState = RS_RF_AUDIO; + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; m_rssiCount = 1U; #if defined(DUMP_M17) openFile(); #endif unsigned short srcId = m_rfLayer3.getSourceUnitId(); unsigned short dstId = m_rfLayer3.getDestinationGroupId(); - bool grp = m_rfLayer3.getIsGroup(); + bool grp = m_rfLayer3.getIsGroup(); std::string source = m_lookup->find(srcId); LogMessage("M17, received RF header from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); @@ -283,7 +380,8 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) } return true; - } else { + } + else { if (m_rfState == RS_RF_LISTENING) { CM17FACCH1 facch; bool valid = false; @@ -326,7 +424,7 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) switch (structure) { case M17_SR_1_4: m_rfLayer3.decode(message, 18U, 0U); - if(m_rfLayer3.getMessageType() == M17_MESSAGE_TYPE_VCALL) + if (m_rfLayer3.getMessageType() == M17_MESSAGE_TYPE_VCALL) m_rfMask = 0x01U; else m_rfMask = 0x00U; @@ -475,9 +573,10 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) LogDebug("M17, AMBE FEC %u/188 (%.1f%%)", errors, float(errors) / 1.88F); CM17Audio audio; - audio.decode(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES + 0U, netData + 5U + 0U); + audio.decode(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES + 0U, netData + 5U + 0U); audio.decode(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES + 18U, netData + 5U + 14U); - } else if (option == M17_LICH_STEAL_FACCH1_1) { + } + else if (option == M17_LICH_STEAL_FACCH1_1) { CM17FACCH1 facch1; bool valid = facch1.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS); if (valid) @@ -495,7 +594,8 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) CM17Audio audio; audio.decode(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES + 18U, netData + 5U + 14U); - } else if (option == M17_LICH_STEAL_FACCH1_2) { + } + else if (option == M17_LICH_STEAL_FACCH1_2) { CAMBEFEC ambe; unsigned int errors = 0U; errors += ambe.regenerateYSFDN(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES); @@ -513,7 +613,8 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) if (valid) facch1.encode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS + M17_FACCH1_LENGTH_BITS); facch1.getRaw(netData + 5U + 14U); - } else { + } + else { CM17FACCH1 facch11; bool valid1 = facch11.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS); if (valid1) @@ -548,6 +649,17 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len) m_display->writeM17RSSI(m_rssi); } + m_rfFrames++; + + // EOT? + if ((fn & 0x8000U) == 0x8000U) { + if (m_rssi != 0U) + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); + } + return true; } @@ -571,7 +683,6 @@ void CM17Control::writeEndRF() m_rfState = RS_RF_LISTENING; m_rfMask = 0x00U; - m_rfLayer3.reset(); m_rfTimeoutTimer.stop(); @@ -591,8 +702,6 @@ void CM17Control::writeEndNet() { m_netState = RS_NET_IDLE; - m_netLayer3.reset(); - m_netTimeoutTimer.stop(); m_networkWatchdog.stop(); m_packetTimer.stop(); @@ -660,8 +769,16 @@ void CM17Control::writeNetwork() // Generate the sync CSync::addM17Sync(start + 2U); + unsigned char setup[M17_LICH_LENGTH_BYTES]; m_netLICH.getLinkSetup(start + 2U); + // Add the CRC + CM17CRC::encodeCRC(start + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); + + // Add the convolution FEC + CM17Convolution conv; + conv.encode(setup, start + 2U + M17_SYNC_LENGTH_BYTES, M17_LICH_LENGTH_BITS + M17_CRC_LENGTH_BITS); + unsigned char temp[M17_FRAME_LENGTH_BYTES]; interleaver(start + 2U, temp); decorrelator(temp, start + 2U); @@ -674,18 +791,53 @@ void CM17Control::writeNetwork() data[0U] = TAG_DATA; data[1U] = 0x00U; + unsigned char* p = data + 2U; + // Generate the sync - CSync::addM17Sync(data + 2U); + CSync::addM17Sync(p); + p += M17_SYNC_LENGTH_BYTES; m_netFrames++; // Add the fragment LICH uint16_t fn = (netData[38U] << 8) + (netData[39U] << 0); - m_netLICH.getFragment(data + 2U, fn & 0x7FFFU); - // Add the data/audio + unsigned char lich[6U]; + m_netLICH.getFragment(lich, fn & 0x7FFFU); - // Add the FEC + unsigned int lich1, lich2, lich3, lich4; + + // XXX TODO + + // Add Golay to the LICH fragment here + CGolay24128::encode24128(lich1); + CGolay24128::encode24128(lich2); + CGolay24128::encode24128(lich3); + CGolay24128::encode24128(lich4); + + ::memcpy(p, &lich1, M17_LICH_FRAGMENT_LENGTH_BYTES / 4U); + p += M17_LICH_FRAGMENT_LENGTH_BYTES / 4U; + + ::memcpy(p, &lich2, M17_LICH_FRAGMENT_LENGTH_BYTES / 4U); + p += M17_LICH_FRAGMENT_LENGTH_BYTES / 4U; + + ::memcpy(p, &lich3, M17_LICH_FRAGMENT_LENGTH_BYTES / 4U); + p += M17_LICH_FRAGMENT_LENGTH_BYTES / 4U; + + ::memcpy(p, &lich4, M17_LICH_FRAGMENT_LENGTH_BYTES / 4U); + p += M17_LICH_FRAGMENT_LENGTH_BYTES / 4U; + + unsigned char payload[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + + // Add the FN and the data/audio + ::memcpy(payload, netData + 38U, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); + + // Add the CRC + CM17CRC::encodeCRC(payload, M17_FN_LENGTH_BITS + M17_PAYLOAD_LENGTH_BITS + M17_CRC_LENGTH_BITS); + + // Add the Convolution FEC + CM17Convolution conv; + conv.encode(payload, p, M17_FN_LENGTH_BITS + M17_PAYLOAD_LENGTH_BITS + M17_CRC_LENGTH_BITS); unsigned char temp[M17_FRAME_LENGTH_BYTES]; interleaver(data + 2U, temp); @@ -794,7 +946,7 @@ void CM17Control::decorrelator(const unsigned char* in, unsigned char* out) cons assert(in != NULL); assert(out != NULL); - for (unsigned int i = M17_SYNC_BYTES_LENGTH; i < M17_FRAME_LENGTH_BYTES; i++) + for (unsigned int i = M17_SYNC_LENGTH_BYTES; i < M17_FRAME_LENGTH_BYTES; i++) out[i] = in[i] ^ SCRAMBLER[i]; } @@ -852,15 +1004,12 @@ void CM17Control::enable(bool enabled) m_rfState = RS_RF_LISTENING; m_rfMask = 0x00U; - m_rfLayer3.reset(); m_rfTimeoutTimer.stop(); // Reset the networking section m_netState = RS_NET_IDLE; - m_netLayer3.reset(); - m_netTimeoutTimer.stop(); m_networkWatchdog.stop(); m_packetTimer.stop(); diff --git a/M17Convolution.h b/M17Convolution.h new file mode 100644 index 0000000..9e7feb4 --- /dev/null +++ b/M17Convolution.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015,2016,2018,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(M17Convolution_H) +#define M17Convolution_H + +#include + +class CM17Convolution { +public: + CM17Convolution(); + ~CM17Convolution(); + + void start(); + void decode(uint8_t s0, uint8_t s1); + void chainback(unsigned char* out, unsigned int nBits); + + void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const; + +private: + uint16_t* m_metrics1; + uint16_t* m_metrics2; + uint16_t* m_oldMetrics; + uint16_t* m_newMetrics; + uint64_t* m_decisions; + uint64_t* m_dp; +}; + +#endif diff --git a/M17Defines.h b/M17Defines.h index e9cb48f..9e061e8 100644 --- a/M17Defines.h +++ b/M17Defines.h @@ -23,12 +23,24 @@ const unsigned int M17_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate const unsigned int M17_FRAME_LENGTH_BITS = 384U; const unsigned int M17_FRAME_LENGTH_BYTES = M17_FRAME_LENGTH_BITS / 8U; -const unsigned int M17_FRAME_LENGTH_SYMBOLS = M17_FRAME_LENGTH_BITS / 2U; - -const unsigned int M17_SYNC_LENGTH_BITS = 16U; -const unsigned int M17_SYNC_LENGTH_SYMBOLS = M17_SYNC_LENGTH_BITS / 2U; const unsigned char M17_SYNC_BYTES[] = {0x32U, 0x43U}; -const unsigned int M17_SYNC_BYTES_LENGTH = 2U; +const unsigned int M17_SYNC_LENGTH_BITS = 16U; +const unsigned int M17_SYNC_LENGTH_BYTES = M17_SYNC_LENGTH_BITS / 8U; + +const unsigned int M17_LICH_LENGTH_BITS = 244U; +const unsigned int M17_LICH_LENGTH_BYTES = 31U; + +const unsigned int M17_LICH_FRAGMENT_LENGTH_BITS = 96U; +const unsigned int M17_LICH_FRAGMENT_LENGTH_BYTES = M17_LICH_FRAGMENT_LENGTH_BITS / 8U; + +const unsigned int M17_PAYLOAD_LENGTH_BITS = 128U; +const unsigned int M17_PAYLOAD_LENGTH_BYTES = M17_PAYLOAD_LENGTH_BITS / 8U; + +const unsigned int M17_FN_LENGTH_BITS = 16U; +const unsigned int M17_FN_LENGTH_BYTES = M17_FN_LENGTH_BITS / 8U; + +const unsigned int M17_CRC_LENGTH_BITS = 16U; +const unsigned int M17_CRC_LENGTH_BYTES = M17_CRC_LENGTH_BITS / 8U; #endif diff --git a/M17LICH.h b/M17LICH.h index 97215cd..c16a58f 100644 --- a/M17LICH.h +++ b/M17LICH.h @@ -27,14 +27,20 @@ public: CM17LICH(); ~CM17LICH(); + void getNetworkData(unsigned char* data); void setNetworkData(const unsigned char* data); std::string getSource() const; std::string getDest() const; unsigned char getDataType() const; + bool isValid() const; + void getLinkSetup(unsigned char* data) const; + void setLinkSetup(const unsigned char* data) const; + void getFragment(unsigned char* data, unsigned short fn); + void setFragment(const unsigned char* data, unsigned short fn); CM17LICH& operator=(const CM17LICH& lich); diff --git a/M17Network.cpp b/M17Network.cpp index 20ee736..6e4d66e 100644 --- a/M17Network.cpp +++ b/M17Network.cpp @@ -18,6 +18,7 @@ #include "M17Network.h" #include "M17Defines.h" +#include "M17Utils.h" #include "Defines.h" #include "Utils.h" #include "Log.h" @@ -28,8 +29,8 @@ const unsigned int BUFFER_LENGTH = 200U; -CM17Network::CM17Network(unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : -m_socket(localPort), +CM17Network::CM17Network(unsigned int port, bool debug) : +m_socket(port), m_addr(), m_addrLen(0U), m_debug(debug), @@ -37,34 +38,69 @@ m_enabled(false), m_outId(0U), m_inId(0U), m_buffer(1000U, "M17 Network"), -m_random() +m_random(), +m_state(M17N_NOTLINKED), +m_reflector(NULL), +m_module(' '), +m_timer(1000U, 5U) { std::random_device rd; std::mt19937 mt(rd()); m_random = mt; - if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) - m_addrLen = 0U; + m_encoded = new unsigned char[6U]; } CM17Network::~CM17Network() { + delete[] m_encoded; } bool CM17Network::open() { - if (m_addrLen == 0U) { - LogError("Unable to resolve the address of the M17 Gateway"); - return false; - } - LogMessage("Opening M17 network connection"); return m_socket.open(m_addr); } +bool CM17Network::link(const std::string& address, unsigned int port, const std::string& reflector, char module) +{ + if (CUDPSocket::lookup(address, port, m_addr, m_addrLen) != 0) { + m_state = M17N_NOTLINKED; + return false; + } + + m_reflector = reflector; + m_module = module; + + CM17Utils::encodeCallsign(m_reflector, m_encoded); + + m_state = M17N_LINKING; + + sendConnect(); + + m_timer.start(); + + return true; +} + +void CM17Network::unlink() +{ + if (m_state != M17N_LINKED) + return; + + m_state = M17N_UNLINKING; + + sendConnect(); + + m_timer.start(); +} + bool CM17Network::write(const unsigned char* data) { + if (m_state != M17N_LINKED) + return false; + assert(data != NULL); unsigned char buffer[100U]; @@ -91,6 +127,23 @@ bool CM17Network::write(const unsigned char* data) void CM17Network::clock(unsigned int ms) { + m_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + switch (m_state) { + case M17N_LINKING: + sendConnect(); + m_timer.start(); + break; + case M17N_UNLINKING: + sendDisconnect(); + m_timer.start(); + break; + default: + m_timer.stop(); + break; + } + } + unsigned char buffer[BUFFER_LENGTH]; sockaddr_storage address; @@ -104,14 +157,42 @@ void CM17Network::clock(unsigned int ms) return; } - if (!m_enabled) - return; - if (m_debug) CUtils::dump(1U, "M17 Network Data Received", buffer, length); - if (::memcmp(buffer + 0U, "M17 ", 4U) != 0) + if (::memcmp(buffer + 0U, "ACKN", 4U) == 0) { + m_timer.stop(); + m_state = M17N_LINKED; + LogMessage("M17, linked to %s", m_reflector.c_str()); return; + } + + if (::memcmp(buffer + 0U, "NACK", 4U) == 0) { + m_timer.stop(); + m_state = M17N_NOTLINKED; + LogMessage("M17, link refused by %s", m_reflector.c_str()); + return; + } + + if (::memcmp(buffer + 0U, "PING", 4U) == 0) { + sendPong(); + return; + } + + if (::memcmp(buffer + 0U, "DISC", 4U) == 0) { + m_timer.stop(); + m_state = M17N_NOTLINKED; + LogMessage("M17, unlinked from %s", m_reflector.c_str()); + return; + } + + if (!m_enabled) + return; + + if (::memcmp(buffer + 0U, "M17 ", 4U) != 0) { + CUtils::dump(2U, "M17, received unknown packet", buffer, length); + return; + } uint16_t id = (buffer[4U] << 8) + (buffer[5U] << 0); if (m_inId == 0U) { @@ -167,3 +248,71 @@ void CM17Network::enable(bool enabled) m_enabled = enabled; } + +void CM17Network::sendConnect() +{ + unsigned char buffer[15U]; + + buffer[0U] = 'C'; + buffer[1U] = 'O'; + buffer[2U] = 'N'; + buffer[3U] = 'N'; + + buffer[4U] = m_encoded[0U]; + buffer[5U] = m_encoded[1U]; + buffer[6U] = m_encoded[2U]; + buffer[7U] = m_encoded[3U]; + buffer[8U] = m_encoded[4U]; + buffer[9U] = m_encoded[5U]; + + buffer[10U] = m_module; + + if (m_debug) + CUtils::dump(1U, "M17 data transmitted", buffer, 11U); + + m_socket.write(buffer, 11U, m_addr, m_addrLen); +} + +void CM17Network::sendPong() +{ + unsigned char buffer[15U]; + + buffer[0U] = 'P'; + buffer[1U] = 'O'; + buffer[2U] = 'N'; + buffer[3U] = 'G'; + + buffer[4U] = m_encoded[0U]; + buffer[5U] = m_encoded[1U]; + buffer[6U] = m_encoded[2U]; + buffer[7U] = m_encoded[3U]; + buffer[8U] = m_encoded[4U]; + buffer[9U] = m_encoded[5U]; + + if (m_debug) + CUtils::dump(1U, "M17 data transmitted", buffer, 10U); + + m_socket.write(buffer, 10U, m_addr, m_addrLen); +} + +void CM17Network::sendDisconnect() +{ + unsigned char buffer[15U]; + + buffer[0U] = 'D'; + buffer[1U] = 'I'; + buffer[2U] = 'S'; + buffer[3U] = 'C'; + + buffer[4U] = m_encoded[0U]; + buffer[5U] = m_encoded[1U]; + buffer[6U] = m_encoded[2U]; + buffer[7U] = m_encoded[3U]; + buffer[8U] = m_encoded[4U]; + buffer[9U] = m_encoded[5U]; + + if (m_debug) + CUtils::dump(1U, "M17 data transmitted", buffer, 10U); + + m_socket.write(buffer, 10U, m_addr, m_addrLen); +} diff --git a/M17Network.h b/M17Network.h index 697cb1a..b1d3710 100644 --- a/M17Network.h +++ b/M17Network.h @@ -22,17 +22,29 @@ #include "M17Defines.h" #include "RingBuffer.h" #include "UDPSocket.h" +#include "Timer.h" #include #include +enum M17NET_STATUS { + M17N_NOTLINKED, + M17N_LINKING, + M17N_LINKED, + M17N_UNLINKING +}; + class CM17Network { public: - CM17Network(unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, bool debug); + CM17Network(unsigned int port, bool debug); ~CM17Network(); bool open(); + bool link(const std::string& address, unsigned int port, const std::string& reflector, char module); + + void unlink(); + void enable(bool enabled); bool write(const unsigned char* data); @@ -55,6 +67,15 @@ private: uint16_t m_inId; CRingBuffer m_buffer; std::mt19937 m_random; + M17NET_STATUS m_state; + std::string m_reflector; + unsigned char* m_encoded; + char m_module; + CTimer m_timer; + + void sendConnect(); + void sendDisconnect(); + void sendPong(); }; #endif diff --git a/M17Utils.h b/M17Utils.h new file mode 100644 index 0000000..025e65a --- /dev/null +++ b/M17Utils.h @@ -0,0 +1,36 @@ +/* + * 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(M17Utils_H) +#define M17Utils_H + +#include + +class CM17Utils { +public: + CM17Utils(); + ~CM17Utils(); + + static void encodeCallsign(const std::string& callsign, unsigned char* data); + + static void decodeCallsign(const unsigned char* data, std::string& callsign); + +private: +}; + +#endif diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 87d40df..a399c3a 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -189,9 +189,12 @@ + + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index f013ec1..8f9a797 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -311,6 +311,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files +