Add M17 reflector linking and start on the frame handling.
This commit is contained in:
parent
b921f99d0a
commit
8946038e0e
10 changed files with 594 additions and 131 deletions
34
M17CRC.h
Normal file
34
M17CRC.h
Normal file
|
@ -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 <cstdint>
|
||||||
|
|
||||||
|
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
|
371
M17Control.cpp
371
M17Control.cpp
|
@ -12,6 +12,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "M17Control.h"
|
#include "M17Control.h"
|
||||||
|
#include "M17Convolution.h"
|
||||||
|
#include "M17CRC.h"
|
||||||
|
#include "Golay24128.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "Sync.h"
|
#include "Sync.h"
|
||||||
#include "Log.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);
|
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) {
|
if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) {
|
||||||
std::string source = m_rfLICH.getSource();
|
std::string source = m_rfLICH.getSource();
|
||||||
std::string dest = m_rfLICH.getDest();
|
std::string dest = m_rfLICH.getDest();
|
||||||
|
|
||||||
if (m_rssi != 0U)
|
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);
|
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) {
|
if (type == TAG_LOST) {
|
||||||
m_rfState = RS_RF_LISTENING;
|
m_rfState = RS_RF_LISTENING;
|
||||||
m_rfMask = 0x00U;
|
m_rfMask = 0x00U;
|
||||||
m_rfLayer3.reset();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,98 +153,193 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len)
|
||||||
decorrelator(data + 2U, temp);
|
decorrelator(data + 2U, temp);
|
||||||
interleaver(temp, data + 2U);
|
interleaver(temp, data + 2U);
|
||||||
|
|
||||||
CM17LICH lich;
|
if (m_rfState == RS_RF_LISTENING) {
|
||||||
bool valid = lich.decode(data + 2U);
|
m_rfMask = 0x00U;
|
||||||
|
|
||||||
if (valid)
|
CM17Convolution conv;
|
||||||
m_rfLastLICH = lich;
|
conv.start();
|
||||||
|
|
||||||
unsigned char usc = m_rfLastLICH.getFCT();
|
unsigned int n = 2U + M17_SYNC_LENGTH_BYTES;
|
||||||
unsigned char option = m_rfLastLICH.getOption();
|
for (unsigned int i = 0U; i < (M17_LICH_LENGTH_BYTES / 2U); i++) {
|
||||||
|
uint8_t s0 = data[n++];
|
||||||
|
uint8_t s1 = data[n++];
|
||||||
|
|
||||||
CM17SACCH sacch;
|
conv.decode(s0, s1);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
bool valid = CM17CRC::checkCRC(frame, M17_LICH_LENGTH_BITS);
|
||||||
data[1U] = 0x00U;
|
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;
|
std::string source = m_rfLICH.getSource();
|
||||||
lich.setRFCT(M17_LICH_RFCT_RDCH);
|
std::string dest = m_rfLICH.getDest();
|
||||||
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);
|
|
||||||
|
|
||||||
lich.setDirection(M17_LICH_DIRECTION_INBOUND);
|
unsigned char dataType = m_rfLICH.getDataType();
|
||||||
netData[0U] = lich.getRaw();
|
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;
|
m_display->writeM17(source.c_str(), dest.c_str(), "R");
|
||||||
sacch.setRAN(m_ran);
|
|
||||||
sacch.setStructure(M17_SR_SINGLE);
|
|
||||||
sacch.setData(SACCH_IDLE);
|
|
||||||
sacch.encode(data + 2U);
|
|
||||||
|
|
||||||
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);
|
// Generate the sync
|
||||||
facch.encode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS + M17_FACCH1_LENGTH_BITS);
|
CSync::addM17Sync(data + 2U);
|
||||||
|
|
||||||
facch.getRaw(netData + 5U + 0U);
|
unsigned char setup[M17_LICH_LENGTH_BYTES];
|
||||||
facch.getRaw(netData + 5U + 14U);
|
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)
|
#if defined(DUMP_M17)
|
||||||
writeFile(data + 2U);
|
writeFile(data + 2U);
|
||||||
#endif
|
#endif
|
||||||
|
@ -250,32 +347,32 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len)
|
||||||
writeQueueRF(data);
|
writeQueueRF(data);
|
||||||
|
|
||||||
if (data[0U] == TAG_EOT) {
|
if (data[0U] == TAG_EOT) {
|
||||||
unsigned short dstId = m_rfLayer3.getDestinationGroupId();
|
std::string source = m_rfLICH.getSource();
|
||||||
bool grp = m_rfLayer3.getIsGroup();
|
std::string dest = m_rfLICH.getDest();
|
||||||
std::string source = m_lookup->find(m_rfLayer3.getSourceUnitId());
|
|
||||||
|
|
||||||
m_rfFrames++;
|
m_rfFrames++;
|
||||||
if (m_rssi != 0U)
|
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
|
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();
|
writeEndRF();
|
||||||
} else {
|
}
|
||||||
m_rfFrames = 0U;
|
else {
|
||||||
m_rfErrs = 0U;
|
m_rfFrames = 0U;
|
||||||
m_rfBits = 1U;
|
m_rfErrs = 0U;
|
||||||
|
m_rfBits = 1U;
|
||||||
m_rfTimeoutTimer.start();
|
m_rfTimeoutTimer.start();
|
||||||
m_rfState = RS_RF_AUDIO;
|
m_rfState = RS_RF_AUDIO;
|
||||||
m_minRSSI = m_rssi;
|
m_minRSSI = m_rssi;
|
||||||
m_maxRSSI = m_rssi;
|
m_maxRSSI = m_rssi;
|
||||||
m_aveRSSI = m_rssi;
|
m_aveRSSI = m_rssi;
|
||||||
m_rssiCount = 1U;
|
m_rssiCount = 1U;
|
||||||
#if defined(DUMP_M17)
|
#if defined(DUMP_M17)
|
||||||
openFile();
|
openFile();
|
||||||
#endif
|
#endif
|
||||||
unsigned short srcId = m_rfLayer3.getSourceUnitId();
|
unsigned short srcId = m_rfLayer3.getSourceUnitId();
|
||||||
unsigned short dstId = m_rfLayer3.getDestinationGroupId();
|
unsigned short dstId = m_rfLayer3.getDestinationGroupId();
|
||||||
bool grp = m_rfLayer3.getIsGroup();
|
bool grp = m_rfLayer3.getIsGroup();
|
||||||
|
|
||||||
std::string source = m_lookup->find(srcId);
|
std::string source = m_lookup->find(srcId);
|
||||||
LogMessage("M17, received RF header from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId);
|
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;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (m_rfState == RS_RF_LISTENING) {
|
if (m_rfState == RS_RF_LISTENING) {
|
||||||
CM17FACCH1 facch;
|
CM17FACCH1 facch;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
@ -326,7 +424,7 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len)
|
||||||
switch (structure) {
|
switch (structure) {
|
||||||
case M17_SR_1_4:
|
case M17_SR_1_4:
|
||||||
m_rfLayer3.decode(message, 18U, 0U);
|
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;
|
m_rfMask = 0x01U;
|
||||||
else
|
else
|
||||||
m_rfMask = 0x00U;
|
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);
|
LogDebug("M17, AMBE FEC %u/188 (%.1f%%)", errors, float(errors) / 1.88F);
|
||||||
|
|
||||||
CM17Audio audio;
|
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);
|
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;
|
CM17FACCH1 facch1;
|
||||||
bool valid = facch1.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS);
|
bool valid = facch1.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS);
|
||||||
if (valid)
|
if (valid)
|
||||||
|
@ -495,7 +594,8 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len)
|
||||||
|
|
||||||
CM17Audio audio;
|
CM17Audio audio;
|
||||||
audio.decode(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES + 18U, netData + 5U + 14U);
|
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;
|
CAMBEFEC ambe;
|
||||||
unsigned int errors = 0U;
|
unsigned int errors = 0U;
|
||||||
errors += ambe.regenerateYSFDN(data + 2U + M17_FSW_LICH_SACCH_LENGTH_BYTES);
|
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)
|
if (valid)
|
||||||
facch1.encode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS + M17_FACCH1_LENGTH_BITS);
|
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);
|
facch1.getRaw(netData + 5U + 14U);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
CM17FACCH1 facch11;
|
CM17FACCH1 facch11;
|
||||||
bool valid1 = facch11.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS);
|
bool valid1 = facch11.decode(data + 2U, M17_FSW_LENGTH_BITS + M17_LICH_LENGTH_BITS + M17_SACCH_LENGTH_BITS);
|
||||||
if (valid1)
|
if (valid1)
|
||||||
|
@ -548,6 +649,17 @@ bool CM17Control::writeModem(unsigned char *data, unsigned int len)
|
||||||
m_display->writeM17RSSI(m_rssi);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,7 +683,6 @@ void CM17Control::writeEndRF()
|
||||||
m_rfState = RS_RF_LISTENING;
|
m_rfState = RS_RF_LISTENING;
|
||||||
|
|
||||||
m_rfMask = 0x00U;
|
m_rfMask = 0x00U;
|
||||||
m_rfLayer3.reset();
|
|
||||||
|
|
||||||
m_rfTimeoutTimer.stop();
|
m_rfTimeoutTimer.stop();
|
||||||
|
|
||||||
|
@ -591,8 +702,6 @@ void CM17Control::writeEndNet()
|
||||||
{
|
{
|
||||||
m_netState = RS_NET_IDLE;
|
m_netState = RS_NET_IDLE;
|
||||||
|
|
||||||
m_netLayer3.reset();
|
|
||||||
|
|
||||||
m_netTimeoutTimer.stop();
|
m_netTimeoutTimer.stop();
|
||||||
m_networkWatchdog.stop();
|
m_networkWatchdog.stop();
|
||||||
m_packetTimer.stop();
|
m_packetTimer.stop();
|
||||||
|
@ -660,8 +769,16 @@ void CM17Control::writeNetwork()
|
||||||
// Generate the sync
|
// Generate the sync
|
||||||
CSync::addM17Sync(start + 2U);
|
CSync::addM17Sync(start + 2U);
|
||||||
|
|
||||||
|
unsigned char setup[M17_LICH_LENGTH_BYTES];
|
||||||
m_netLICH.getLinkSetup(start + 2U);
|
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];
|
unsigned char temp[M17_FRAME_LENGTH_BYTES];
|
||||||
interleaver(start + 2U, temp);
|
interleaver(start + 2U, temp);
|
||||||
decorrelator(temp, start + 2U);
|
decorrelator(temp, start + 2U);
|
||||||
|
@ -674,18 +791,53 @@ void CM17Control::writeNetwork()
|
||||||
data[0U] = TAG_DATA;
|
data[0U] = TAG_DATA;
|
||||||
data[1U] = 0x00U;
|
data[1U] = 0x00U;
|
||||||
|
|
||||||
|
unsigned char* p = data + 2U;
|
||||||
|
|
||||||
// Generate the sync
|
// Generate the sync
|
||||||
CSync::addM17Sync(data + 2U);
|
CSync::addM17Sync(p);
|
||||||
|
p += M17_SYNC_LENGTH_BYTES;
|
||||||
|
|
||||||
m_netFrames++;
|
m_netFrames++;
|
||||||
|
|
||||||
// Add the fragment LICH
|
// Add the fragment LICH
|
||||||
uint16_t fn = (netData[38U] << 8) + (netData[39U] << 0);
|
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];
|
unsigned char temp[M17_FRAME_LENGTH_BYTES];
|
||||||
interleaver(data + 2U, temp);
|
interleaver(data + 2U, temp);
|
||||||
|
@ -794,7 +946,7 @@ void CM17Control::decorrelator(const unsigned char* in, unsigned char* out) cons
|
||||||
assert(in != NULL);
|
assert(in != NULL);
|
||||||
assert(out != 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];
|
out[i] = in[i] ^ SCRAMBLER[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,15 +1004,12 @@ void CM17Control::enable(bool enabled)
|
||||||
m_rfState = RS_RF_LISTENING;
|
m_rfState = RS_RF_LISTENING;
|
||||||
|
|
||||||
m_rfMask = 0x00U;
|
m_rfMask = 0x00U;
|
||||||
m_rfLayer3.reset();
|
|
||||||
|
|
||||||
m_rfTimeoutTimer.stop();
|
m_rfTimeoutTimer.stop();
|
||||||
|
|
||||||
// Reset the networking section
|
// Reset the networking section
|
||||||
m_netState = RS_NET_IDLE;
|
m_netState = RS_NET_IDLE;
|
||||||
|
|
||||||
m_netLayer3.reset();
|
|
||||||
|
|
||||||
m_netTimeoutTimer.stop();
|
m_netTimeoutTimer.stop();
|
||||||
m_networkWatchdog.stop();
|
m_networkWatchdog.stop();
|
||||||
m_packetTimer.stop();
|
m_packetTimer.stop();
|
||||||
|
|
44
M17Convolution.h
Normal file
44
M17Convolution.h
Normal file
|
@ -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 <cstdint>
|
||||||
|
|
||||||
|
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
|
22
M17Defines.h
22
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_BITS = 384U;
|
||||||
const unsigned int M17_FRAME_LENGTH_BYTES = M17_FRAME_LENGTH_BITS / 8U;
|
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 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
|
#endif
|
||||||
|
|
|
@ -27,14 +27,20 @@ public:
|
||||||
CM17LICH();
|
CM17LICH();
|
||||||
~CM17LICH();
|
~CM17LICH();
|
||||||
|
|
||||||
|
void getNetworkData(unsigned char* data);
|
||||||
void setNetworkData(const unsigned char* data);
|
void setNetworkData(const unsigned char* data);
|
||||||
|
|
||||||
std::string getSource() const;
|
std::string getSource() const;
|
||||||
std::string getDest() const;
|
std::string getDest() const;
|
||||||
unsigned char getDataType() const;
|
unsigned char getDataType() const;
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
void getLinkSetup(unsigned char* data) const;
|
void getLinkSetup(unsigned char* data) const;
|
||||||
|
void setLinkSetup(const unsigned char* data) const;
|
||||||
|
|
||||||
void getFragment(unsigned char* data, unsigned short fn);
|
void getFragment(unsigned char* data, unsigned short fn);
|
||||||
|
void setFragment(const unsigned char* data, unsigned short fn);
|
||||||
|
|
||||||
CM17LICH& operator=(const CM17LICH& lich);
|
CM17LICH& operator=(const CM17LICH& lich);
|
||||||
|
|
||||||
|
|
177
M17Network.cpp
177
M17Network.cpp
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "M17Network.h"
|
#include "M17Network.h"
|
||||||
#include "M17Defines.h"
|
#include "M17Defines.h"
|
||||||
|
#include "M17Utils.h"
|
||||||
#include "Defines.h"
|
#include "Defines.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
@ -28,8 +29,8 @@
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
const unsigned int BUFFER_LENGTH = 200U;
|
||||||
|
|
||||||
CM17Network::CM17Network(unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) :
|
CM17Network::CM17Network(unsigned int port, bool debug) :
|
||||||
m_socket(localPort),
|
m_socket(port),
|
||||||
m_addr(),
|
m_addr(),
|
||||||
m_addrLen(0U),
|
m_addrLen(0U),
|
||||||
m_debug(debug),
|
m_debug(debug),
|
||||||
|
@ -37,34 +38,69 @@ m_enabled(false),
|
||||||
m_outId(0U),
|
m_outId(0U),
|
||||||
m_inId(0U),
|
m_inId(0U),
|
||||||
m_buffer(1000U, "M17 Network"),
|
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::random_device rd;
|
||||||
std::mt19937 mt(rd());
|
std::mt19937 mt(rd());
|
||||||
m_random = mt;
|
m_random = mt;
|
||||||
|
|
||||||
if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0)
|
m_encoded = new unsigned char[6U];
|
||||||
m_addrLen = 0U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CM17Network::~CM17Network()
|
CM17Network::~CM17Network()
|
||||||
{
|
{
|
||||||
|
delete[] m_encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CM17Network::open()
|
bool CM17Network::open()
|
||||||
{
|
{
|
||||||
if (m_addrLen == 0U) {
|
|
||||||
LogError("Unable to resolve the address of the M17 Gateway");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage("Opening M17 network connection");
|
LogMessage("Opening M17 network connection");
|
||||||
|
|
||||||
return m_socket.open(m_addr);
|
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)
|
bool CM17Network::write(const unsigned char* data)
|
||||||
{
|
{
|
||||||
|
if (m_state != M17N_LINKED)
|
||||||
|
return false;
|
||||||
|
|
||||||
assert(data != NULL);
|
assert(data != NULL);
|
||||||
|
|
||||||
unsigned char buffer[100U];
|
unsigned char buffer[100U];
|
||||||
|
@ -91,6 +127,23 @@ bool CM17Network::write(const unsigned char* data)
|
||||||
|
|
||||||
void CM17Network::clock(unsigned int ms)
|
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];
|
unsigned char buffer[BUFFER_LENGTH];
|
||||||
|
|
||||||
sockaddr_storage address;
|
sockaddr_storage address;
|
||||||
|
@ -104,14 +157,42 @@ void CM17Network::clock(unsigned int ms)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
CUtils::dump(1U, "M17 Network Data Received", buffer, length);
|
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;
|
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);
|
uint16_t id = (buffer[4U] << 8) + (buffer[5U] << 0);
|
||||||
if (m_inId == 0U) {
|
if (m_inId == 0U) {
|
||||||
|
@ -167,3 +248,71 @@ void CM17Network::enable(bool enabled)
|
||||||
|
|
||||||
m_enabled = 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);
|
||||||
|
}
|
||||||
|
|
23
M17Network.h
23
M17Network.h
|
@ -22,17 +22,29 @@
|
||||||
#include "M17Defines.h"
|
#include "M17Defines.h"
|
||||||
#include "RingBuffer.h"
|
#include "RingBuffer.h"
|
||||||
#include "UDPSocket.h"
|
#include "UDPSocket.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum M17NET_STATUS {
|
||||||
|
M17N_NOTLINKED,
|
||||||
|
M17N_LINKING,
|
||||||
|
M17N_LINKED,
|
||||||
|
M17N_UNLINKING
|
||||||
|
};
|
||||||
|
|
||||||
class CM17Network {
|
class CM17Network {
|
||||||
public:
|
public:
|
||||||
CM17Network(unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, bool debug);
|
CM17Network(unsigned int port, bool debug);
|
||||||
~CM17Network();
|
~CM17Network();
|
||||||
|
|
||||||
bool open();
|
bool open();
|
||||||
|
|
||||||
|
bool link(const std::string& address, unsigned int port, const std::string& reflector, char module);
|
||||||
|
|
||||||
|
void unlink();
|
||||||
|
|
||||||
void enable(bool enabled);
|
void enable(bool enabled);
|
||||||
|
|
||||||
bool write(const unsigned char* data);
|
bool write(const unsigned char* data);
|
||||||
|
@ -55,6 +67,15 @@ private:
|
||||||
uint16_t m_inId;
|
uint16_t m_inId;
|
||||||
CRingBuffer<unsigned char> m_buffer;
|
CRingBuffer<unsigned char> m_buffer;
|
||||||
std::mt19937 m_random;
|
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
|
#endif
|
||||||
|
|
36
M17Utils.h
Normal file
36
M17Utils.h
Normal file
|
@ -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 <string>
|
||||||
|
|
||||||
|
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
|
|
@ -189,9 +189,12 @@
|
||||||
<ClInclude Include="LCDproc.h" />
|
<ClInclude Include="LCDproc.h" />
|
||||||
<ClInclude Include="Log.h" />
|
<ClInclude Include="Log.h" />
|
||||||
<ClInclude Include="M17Control.h" />
|
<ClInclude Include="M17Control.h" />
|
||||||
|
<ClInclude Include="M17Convolution.h" />
|
||||||
|
<ClInclude Include="M17CRC.h" />
|
||||||
<ClInclude Include="M17Defines.h" />
|
<ClInclude Include="M17Defines.h" />
|
||||||
<ClInclude Include="M17LICH.h" />
|
<ClInclude Include="M17LICH.h" />
|
||||||
<ClInclude Include="M17Network.h" />
|
<ClInclude Include="M17Network.h" />
|
||||||
|
<ClInclude Include="M17Utils.h" />
|
||||||
<ClInclude Include="MMDVMHost.h" />
|
<ClInclude Include="MMDVMHost.h" />
|
||||||
<ClInclude Include="Modem.h" />
|
<ClInclude Include="Modem.h" />
|
||||||
<ClInclude Include="ModemSerialPort.h" />
|
<ClInclude Include="ModemSerialPort.h" />
|
||||||
|
|
|
@ -311,6 +311,15 @@
|
||||||
<ClInclude Include="M17LICH.h">
|
<ClInclude Include="M17LICH.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="M17CRC.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="M17Convolution.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="M17Utils.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="BPTC19696.cpp">
|
<ClCompile Include="BPTC19696.cpp">
|
||||||
|
|
Loading…
Reference in a new issue