MMDVMHost-Private/DMRNetwork.cpp

432 lines
9.5 KiB
C++
Raw Normal View History

2016-01-14 18:45:04 +00:00
/*
* Copyright (C) 2015-2020 by Jonathan Naylor G4KLX
2016-01-14 18:45:04 +00:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DMRNetwork.h"
2016-02-15 18:45:57 +00:00
2016-01-14 18:45:04 +00:00
#include "StopWatch.h"
#include "Utils.h"
#include "Log.h"
2016-03-07 20:21:55 +00:00
#include <cstdio>
2016-01-14 18:45:04 +00:00
#include <cassert>
2018-01-10 19:05:59 +00:00
#include <cstring>
2018-07-04 18:00:51 +00:00
#include <cstdlib>
2016-01-14 18:45:04 +00:00
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
2016-01-14 18:45:04 +00:00
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) :
m_addressStr(address),
2016-01-14 18:45:04 +00:00
m_address(),
m_port(port),
m_id(NULL),
2016-03-08 17:26:51 +00:00
m_duplex(duplex),
2016-01-14 18:45:04 +00:00
m_version(version),
2016-03-08 17:26:51 +00:00
m_debug(debug),
m_socket(local),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_hwType(hwType),
2016-01-14 18:45:04 +00:00
m_buffer(NULL),
m_streamId(NULL),
2018-02-08 20:25:03 +00:00
m_rxData(1000U, "DMR Network"),
m_beacon(false),
m_random(),
2016-01-14 18:45:04 +00:00
m_callsign(),
m_rxFrequency(0U),
m_txFrequency(0U),
m_power(0U),
m_colorCode(0U),
m_pingTimer(1000U, 10U)
2016-01-14 18:45:04 +00:00
{
assert(!address.empty());
assert(port > 0U);
assert(id > 1000U);
m_address = CUDPSocket::lookup(address);
2018-02-08 20:25:03 +00:00
m_buffer = new unsigned char[BUFFER_LENGTH];
m_id = new uint8_t[4U];
m_streamId = new uint32_t[2U];
2016-01-14 18:45:04 +00:00
m_id[0U] = id >> 24;
m_id[1U] = id >> 16;
m_id[2U] = id >> 8;
m_id[3U] = id >> 0;
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
m_streamId[0U] = dist(m_random);
m_streamId[1U] = dist(m_random);
2016-01-14 18:45:04 +00:00
}
CDMRNetwork::~CDMRNetwork()
2016-01-14 18:45:04 +00:00
{
delete[] m_buffer;
2016-01-14 18:45:04 +00:00
delete[] m_streamId;
delete[] m_id;
}
void CDMRNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode)
2016-01-14 18:45:04 +00:00
{
m_callsign = callsign;
m_rxFrequency = rxFrequency;
m_txFrequency = txFrequency;
m_power = power;
m_colorCode = colorCode;
}
bool CDMRNetwork::open()
2016-01-14 18:45:04 +00:00
{
LogMessage("DMR, Opening DMR Network");
2018-07-04 18:00:51 +00:00
bool ret = m_socket.open();
2018-07-04 18:00:51 +00:00
if (ret)
m_pingTimer.start();
2016-01-14 18:45:04 +00:00
return ret;
2016-01-14 18:45:04 +00:00
}
void CDMRNetwork::enable(bool enabled)
{
if (!enabled && m_enabled)
m_rxData.clear();
m_enabled = enabled;
}
bool CDMRNetwork::read(CDMRData& data)
2016-01-14 18:45:04 +00:00
{
2018-02-08 20:25:03 +00:00
if (m_rxData.isEmpty())
return false;
2018-02-08 20:25:03 +00:00
unsigned char length = 0U;
m_rxData.getData(&length, 1U);
m_rxData.getData(m_buffer, length);
2018-02-08 20:25:03 +00:00
// Is this a data packet?
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
return false;
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
unsigned char seqNo = m_buffer[4U];
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0);
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
// DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
return false;
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
2018-02-08 20:25:03 +00:00
FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP;
data.setSeqNo(seqNo);
data.setSlotNo(slotNo);
data.setSrcId(srcId);
data.setDstId(dstId);
data.setFLCO(flco);
bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U;
bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U;
if (dataSync) {
unsigned char dataType = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(dataType);
data.setN(0U);
} else if (voiceSync) {
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE_SYNC);
data.setN(0U);
} else {
unsigned char n = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE);
data.setN(n);
2016-01-14 18:45:04 +00:00
}
2018-02-08 20:25:03 +00:00
return true;
2016-01-14 18:45:04 +00:00
}
bool CDMRNetwork::write(const CDMRData& data)
2016-01-14 18:45:04 +00:00
{
unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH);
buffer[0U] = 'D';
buffer[1U] = 'M';
buffer[2U] = 'R';
buffer[3U] = 'D';
unsigned int srcId = data.getSrcId();
buffer[5U] = srcId >> 16;
buffer[6U] = srcId >> 8;
buffer[7U] = srcId >> 0;
unsigned int dstId = data.getDstId();
buffer[8U] = dstId >> 16;
buffer[9U] = dstId >> 8;
buffer[10U] = dstId >> 0;
::memcpy(buffer + 11U, m_id, 4U);
unsigned int slotNo = data.getSlotNo();
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
2016-01-14 18:45:04 +00:00
buffer[15U] = slotNo == 1U ? 0x00U : 0x80U;
FLCO flco = data.getFLCO();
buffer[15U] |= flco == FLCO_GROUP ? 0x00U : 0x40U;
unsigned int slotIndex = slotNo - 1U;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
2016-01-14 18:45:04 +00:00
unsigned char dataType = data.getDataType();
if (dataType == DT_VOICE_SYNC) {
buffer[15U] |= 0x10U;
} else if (dataType == DT_VOICE) {
buffer[15U] |= data.getN();
} else {
2019-12-15 22:22:47 +00:00
if (dataType == DT_VOICE_LC_HEADER)
m_streamId[slotIndex] = dist(m_random);
2018-02-08 20:34:18 +00:00
2019-12-15 22:22:47 +00:00
if (dataType == DT_CSBK || dataType == DT_DATA_HEADER)
m_streamId[slotIndex] = dist(m_random);
2016-02-29 06:45:40 +00:00
2016-01-14 18:45:04 +00:00
buffer[15U] |= (0x20U | dataType);
}
buffer[4U] = data.getSeqNo();
::memcpy(buffer + 16U, m_streamId + slotIndex, 4U);
data.getData(buffer + 20U);
buffer[53U] = data.getBER();
2017-01-05 19:15:10 +00:00
buffer[54U] = data.getRSSI();
if (m_debug)
CUtils::dump(1U, "Network Transmitted", buffer, HOMEBREW_DATA_PACKET_LENGTH);
2019-12-15 22:22:47 +00:00
write(buffer, HOMEBREW_DATA_PACKET_LENGTH);
return true;
2016-01-14 18:45:04 +00:00
}
bool CDMRNetwork::writeRadioPosition(unsigned int id, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRG", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
::memcpy(buffer + 7U, data + 2U, 7U);
return write(buffer, 14U);
}
bool CDMRNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRA", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
buffer[7U] = type;
::memcpy(buffer + 8U, data + 2U, 7U);
return write(buffer, 15U);
}
void CDMRNetwork::close()
2016-01-14 18:45:04 +00:00
{
LogMessage("DMR, Closing DMR Network");
2016-01-14 18:45:04 +00:00
m_socket.close();
}
void CDMRNetwork::clock(unsigned int ms)
2016-01-14 18:45:04 +00:00
{
m_pingTimer.clock(ms);
if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) {
writeConfig();
m_pingTimer.start();
}
2016-01-14 18:45:04 +00:00
in_addr address;
unsigned int port;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
if (length <= 0)
2016-05-04 07:06:12 +00:00
return;
2016-01-14 18:45:04 +00:00
if (m_address.s_addr != address.s_addr || m_port != port) {
LogMessage("DMR, packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
return;
2016-01-14 18:45:04 +00:00
}
if (!m_enabled)
return;
2016-01-14 18:45:04 +00:00
if (m_debug)
CUtils::dump(1U, "Network Received", m_buffer, length);
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
unsigned char len = length;
m_rxData.addData(&len, 1U);
m_rxData.addData(m_buffer, len);
} else if (::memcmp(m_buffer, "DMRP", 4U) == 0) {
;
} else if (::memcmp(m_buffer, "DMRB", 4U) == 0) {
m_beacon = true;
} else {
CUtils::dump("DMR, unknown packet from the DMR Gateway", m_buffer, length);
2016-01-14 18:45:04 +00:00
}
}
bool CDMRNetwork::writeConfig()
2016-01-14 18:45:04 +00:00
{
const char* software;
char slots = '0';
2016-03-08 17:26:51 +00:00
if (m_duplex) {
if (m_slot1 && m_slot2)
slots = '3';
else if (m_slot1 && !m_slot2)
slots = '1';
else if (!m_slot1 && m_slot2)
slots = '2';
2018-01-10 19:05:59 +00:00
switch (m_hwType) {
case HWT_MMDVM:
software = "MMDVM";
break;
case HWT_MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
2018-03-05 22:46:01 +00:00
case HWT_MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HWT_NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
default:
software = "MMDVM_Unknown";
break;
}
2016-03-08 17:26:51 +00:00
} else {
slots = '4';
switch (m_hwType) {
case HWT_MMDVM:
software = "MMDVM_DMO";
break;
case HWT_DVMEGA:
software = "MMDVM_DVMega";
break;
case HWT_MMDVM_ZUMSPOT:
software = "MMDVM_ZUMspot";
break;
case HWT_MMDVM_HS_HAT:
software = "MMDVM_MMDVM_HS_Hat";
break;
2018-03-05 21:04:01 +00:00
case HWT_MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HWT_NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
2018-05-05 13:42:01 +00:00
case HWT_NANO_DV:
software = "MMDVM_Nano_DV";
break;
2019-05-30 01:22:53 +00:00
case HWT_D2RG_MMDVM_HS:
software = "MMDVM_D2RG_MMDVM_HS";
break;
case HWT_MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
2020-01-08 09:58:41 +00:00
case HWT_OPENGD77_HS:
software = "MMDVM_OpenGD77_HS";
break;
default:
software = "MMDVM_Unknown";
break;
}
2016-03-08 17:26:51 +00:00
}
char buffer[100U];
2016-01-14 18:45:04 +00:00
::memcpy(buffer + 0U, "DMRC", 4U);
2016-01-14 18:45:04 +00:00
::memcpy(buffer + 4U, m_id, 4U);
::sprintf(buffer + 8U, "%02u%c%-40.40s%-40.40s", m_colorCode, slots, m_version, software);
2016-01-14 18:45:04 +00:00
return write((unsigned char*)buffer, 91U);
2016-01-14 18:45:04 +00:00
}
bool CDMRNetwork::wantsBeacon()
2016-01-14 18:45:04 +00:00
{
bool beacon = m_beacon;
m_beacon = false;
return beacon;
}
bool CDMRNetwork::write(const unsigned char* data, unsigned int length)
2016-01-14 18:45:04 +00:00
{
assert(data != NULL);
assert(length > 0U);
// if (m_debug)
// CUtils::dump(1U, "Network Transmitted", data, length);
2016-01-14 18:45:04 +00:00
2016-05-10 06:20:38 +00:00
bool ret = m_socket.write(data, length, m_address, m_port);
if (!ret) {
LogError("DMR, Socket has failed when writing data to the master, retrying connection");
2016-05-10 06:20:38 +00:00
return false;
}
return true;
2016-01-14 18:45:04 +00:00
}