mirror of
https://github.com/g4klx/MMDVMHost
synced 2025-12-22 08:05:49 +08:00
Simplify the Host to DMR Gateway protocol.
This commit is contained in:
344
DMRNetwork.cpp
344
DMRNetwork.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2019 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015-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
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "DMRNetwork.h"
|
||||
|
||||
#include "StopWatch.h"
|
||||
#include "SHA256.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
@@ -33,12 +32,11 @@ const unsigned int BUFFER_LENGTH = 500U;
|
||||
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
|
||||
|
||||
|
||||
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) :
|
||||
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),
|
||||
m_address(),
|
||||
m_port(port),
|
||||
m_id(NULL),
|
||||
m_password(password),
|
||||
m_duplex(duplex),
|
||||
m_version(version),
|
||||
m_debug(debug),
|
||||
@@ -47,37 +45,25 @@ m_enabled(false),
|
||||
m_slot1(slot1),
|
||||
m_slot2(slot2),
|
||||
m_hwType(hwType),
|
||||
m_status(WAITING_CONNECT),
|
||||
m_retryTimer(1000U, 10U),
|
||||
m_timeoutTimer(1000U, 60U),
|
||||
m_buffer(NULL),
|
||||
m_salt(NULL),
|
||||
m_streamId(NULL),
|
||||
m_rxData(1000U, "DMR Network"),
|
||||
m_options(),
|
||||
m_beacon(false),
|
||||
m_random(),
|
||||
m_callsign(),
|
||||
m_rxFrequency(0U),
|
||||
m_txFrequency(0U),
|
||||
m_power(0U),
|
||||
m_colorCode(0U),
|
||||
m_latitude(0.0F),
|
||||
m_longitude(0.0F),
|
||||
m_height(0),
|
||||
m_location(),
|
||||
m_description(),
|
||||
m_url(),
|
||||
m_beacon(false),
|
||||
m_random()
|
||||
m_pingTimer(1000U, 10U)
|
||||
{
|
||||
assert(!address.empty());
|
||||
assert(port > 0U);
|
||||
assert(id > 1000U);
|
||||
assert(!password.empty());
|
||||
|
||||
m_address = CUDPSocket::lookup(address);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
m_salt = new unsigned char[sizeof(uint32_t)];
|
||||
m_id = new uint8_t[4U];
|
||||
m_streamId = new uint32_t[2U];
|
||||
|
||||
@@ -98,43 +84,29 @@ m_random()
|
||||
CDMRNetwork::~CDMRNetwork()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
delete[] m_salt;
|
||||
delete[] m_streamId;
|
||||
delete[] m_id;
|
||||
}
|
||||
|
||||
void CDMRNetwork::setOptions(const std::string& options)
|
||||
{
|
||||
m_options = options;
|
||||
}
|
||||
|
||||
void CDMRNetwork::setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url)
|
||||
void CDMRNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode)
|
||||
{
|
||||
m_callsign = callsign;
|
||||
m_rxFrequency = rxFrequency;
|
||||
m_txFrequency = txFrequency;
|
||||
m_power = power;
|
||||
m_colorCode = colorCode;
|
||||
m_latitude = latitude;
|
||||
m_longitude = longitude;
|
||||
m_height = height;
|
||||
m_location = location;
|
||||
m_description = description;
|
||||
m_url = url;
|
||||
}
|
||||
|
||||
bool CDMRNetwork::open()
|
||||
{
|
||||
LogMessage("DMR, Opening DMR Network");
|
||||
|
||||
if (m_address.s_addr == INADDR_NONE)
|
||||
m_address = CUDPSocket::lookup(m_addressStr);
|
||||
bool ret = m_socket.open();
|
||||
|
||||
m_status = WAITING_CONNECT;
|
||||
m_timeoutTimer.stop();
|
||||
m_retryTimer.start();
|
||||
if (ret)
|
||||
m_pingTimer.start();
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CDMRNetwork::enable(bool enabled)
|
||||
@@ -147,9 +119,6 @@ void CDMRNetwork::enable(bool enabled)
|
||||
|
||||
bool CDMRNetwork::read(CDMRData& data)
|
||||
{
|
||||
if (m_status != RUNNING)
|
||||
return false;
|
||||
|
||||
if (m_rxData.isEmpty())
|
||||
return false;
|
||||
|
||||
@@ -211,9 +180,6 @@ bool CDMRNetwork::read(CDMRData& data)
|
||||
|
||||
bool CDMRNetwork::write(const CDMRData& data)
|
||||
{
|
||||
if (m_status != RUNNING)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH];
|
||||
::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH);
|
||||
|
||||
@@ -285,264 +251,79 @@ bool CDMRNetwork::write(const CDMRData& data)
|
||||
|
||||
bool CDMRNetwork::writeRadioPosition(unsigned int id, const unsigned char* data)
|
||||
{
|
||||
if (m_status != RUNNING)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[20U];
|
||||
|
||||
::memcpy(buffer + 0U, "DMRG", 4U);
|
||||
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
buffer[4U] = id >> 16;
|
||||
buffer[5U] = id >> 8;
|
||||
buffer[6U] = id >> 0;
|
||||
|
||||
buffer[8U] = id >> 16;
|
||||
buffer[9U] = id >> 8;
|
||||
buffer[10U] = id >> 0;
|
||||
::memcpy(buffer + 7U, data + 2U, 7U);
|
||||
|
||||
::memcpy(buffer + 11U, data + 2U, 7U);
|
||||
|
||||
return write(buffer, 18U);
|
||||
return write(buffer, 14U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
|
||||
{
|
||||
if (m_status != RUNNING)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[20U];
|
||||
|
||||
::memcpy(buffer + 0U, "DMRA", 4U);
|
||||
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
buffer[4U] = id >> 16;
|
||||
buffer[5U] = id >> 8;
|
||||
buffer[6U] = id >> 0;
|
||||
|
||||
buffer[8U] = id >> 16;
|
||||
buffer[9U] = id >> 8;
|
||||
buffer[10U] = id >> 0;
|
||||
buffer[7U] = type;
|
||||
|
||||
buffer[11U] = type;
|
||||
::memcpy(buffer + 8U, data + 2U, 7U);
|
||||
|
||||
::memcpy(buffer + 12U, data + 2U, 7U);
|
||||
|
||||
return write(buffer, 19U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeHomePosition(float latitude, float longitude)
|
||||
{
|
||||
m_latitude = latitude;
|
||||
m_longitude = longitude;
|
||||
|
||||
if (m_status != RUNNING)
|
||||
return false;
|
||||
|
||||
char buffer[50U];
|
||||
|
||||
::memcpy(buffer + 0U, "RPTG", 4U);
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
|
||||
::sprintf(buffer + 8U, "%08f%09f", latitude, longitude);
|
||||
|
||||
return write((unsigned char*)buffer, 25U);
|
||||
return write(buffer, 15U);
|
||||
}
|
||||
|
||||
void CDMRNetwork::close()
|
||||
{
|
||||
LogMessage("DMR, Closing DMR Network");
|
||||
|
||||
if (m_status == RUNNING) {
|
||||
unsigned char buffer[9U];
|
||||
::memcpy(buffer + 0U, "RPTCL", 5U);
|
||||
::memcpy(buffer + 5U, m_id, 4U);
|
||||
write(buffer, 9U);
|
||||
}
|
||||
|
||||
m_socket.close();
|
||||
|
||||
m_retryTimer.stop();
|
||||
m_timeoutTimer.stop();
|
||||
}
|
||||
|
||||
void CDMRNetwork::clock(unsigned int ms)
|
||||
{
|
||||
if (m_status == WAITING_CONNECT) {
|
||||
m_retryTimer.clock(ms);
|
||||
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
|
||||
bool ret = m_socket.open();
|
||||
if (ret) {
|
||||
ret = writeLogin();
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
m_status = WAITING_LOGIN;
|
||||
m_timeoutTimer.start();
|
||||
}
|
||||
|
||||
m_retryTimer.start();
|
||||
}
|
||||
|
||||
return;
|
||||
m_pingTimer.clock(ms);
|
||||
if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) {
|
||||
writeConfig();
|
||||
m_pingTimer.start();
|
||||
}
|
||||
|
||||
in_addr address;
|
||||
unsigned int port;
|
||||
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
|
||||
if (length < 0) {
|
||||
LogError("DMR, Socket has failed, retrying connection to the master");
|
||||
close();
|
||||
open();
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// if (m_debug && length > 0)
|
||||
// CUtils::dump(1U, "Network Received", m_buffer, length);
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
if (length > 0 && m_address.s_addr == address.s_addr && m_port == port) {
|
||||
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
|
||||
if (m_enabled) {
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Network Received", m_buffer, length);
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Network Received", m_buffer, length);
|
||||
|
||||
unsigned char len = length;
|
||||
m_rxData.addData(&len, 1U);
|
||||
m_rxData.addData(m_buffer, len);
|
||||
}
|
||||
} else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) {
|
||||
if (m_status == RUNNING) {
|
||||
LogWarning("DMR, Login to the master has failed, retrying login ...");
|
||||
m_status = WAITING_LOGIN;
|
||||
m_timeoutTimer.start();
|
||||
m_retryTimer.start();
|
||||
} else {
|
||||
/* Once the modem death spiral has been prevented in Modem.cpp
|
||||
the Network sometimes times out and reaches here.
|
||||
We want it to reconnect so... */
|
||||
LogError("DMR, Login to the master has failed, retrying network ...");
|
||||
close();
|
||||
open();
|
||||
return;
|
||||
}
|
||||
} else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) {
|
||||
switch (m_status) {
|
||||
case WAITING_LOGIN:
|
||||
LogDebug("DMR, Sending authorisation");
|
||||
::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t));
|
||||
writeAuthorisation();
|
||||
m_status = WAITING_AUTHORISATION;
|
||||
m_timeoutTimer.start();
|
||||
m_retryTimer.start();
|
||||
break;
|
||||
case WAITING_AUTHORISATION:
|
||||
LogDebug("DMR, Sending configuration");
|
||||
writeConfig();
|
||||
m_status = WAITING_CONFIG;
|
||||
m_timeoutTimer.start();
|
||||
m_retryTimer.start();
|
||||
break;
|
||||
case WAITING_CONFIG:
|
||||
if (m_options.empty()) {
|
||||
LogMessage("DMR, Logged into the master successfully");
|
||||
m_status = RUNNING;
|
||||
} else {
|
||||
LogDebug("DMR, Sending options");
|
||||
writeOptions();
|
||||
m_status = WAITING_OPTIONS;
|
||||
}
|
||||
m_timeoutTimer.start();
|
||||
m_retryTimer.start();
|
||||
break;
|
||||
case WAITING_OPTIONS:
|
||||
LogMessage("DMR, Logged into the master successfully");
|
||||
m_status = RUNNING;
|
||||
m_timeoutTimer.start();
|
||||
m_retryTimer.start();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) {
|
||||
LogError("DMR, Master is closing down");
|
||||
close();
|
||||
open();
|
||||
} else if (::memcmp(m_buffer, "MSTPONG", 7U) == 0) {
|
||||
m_timeoutTimer.start();
|
||||
} else if (::memcmp(m_buffer, "RPTSBKN", 7U) == 0) {
|
||||
m_beacon = true;
|
||||
} else {
|
||||
CUtils::dump("Unknown packet from the master", 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);
|
||||
}
|
||||
|
||||
m_retryTimer.clock(ms);
|
||||
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
|
||||
switch (m_status) {
|
||||
case WAITING_LOGIN:
|
||||
writeLogin();
|
||||
break;
|
||||
case WAITING_AUTHORISATION:
|
||||
writeAuthorisation();
|
||||
break;
|
||||
case WAITING_OPTIONS:
|
||||
writeOptions();
|
||||
break;
|
||||
case WAITING_CONFIG:
|
||||
writeConfig();
|
||||
break;
|
||||
case RUNNING:
|
||||
writePing();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_retryTimer.start();
|
||||
}
|
||||
|
||||
m_timeoutTimer.clock(ms);
|
||||
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
|
||||
LogError("DMR, Connection to the master has timed out, retrying connection");
|
||||
close();
|
||||
open();
|
||||
}
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeLogin()
|
||||
{
|
||||
unsigned char buffer[8U];
|
||||
|
||||
::memcpy(buffer + 0U, "RPTL", 4U);
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
|
||||
return write(buffer, 8U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeAuthorisation()
|
||||
{
|
||||
size_t size = m_password.size();
|
||||
|
||||
unsigned char* in = new unsigned char[size + sizeof(uint32_t)];
|
||||
::memcpy(in, m_salt, sizeof(uint32_t));
|
||||
for (size_t i = 0U; i < size; i++)
|
||||
in[i + sizeof(uint32_t)] = m_password.at(i);
|
||||
|
||||
unsigned char out[40U];
|
||||
::memcpy(out + 0U, "RPTK", 4U);
|
||||
::memcpy(out + 4U, m_id, 4U);
|
||||
|
||||
CSHA256 sha256;
|
||||
sha256.buffer(in, (unsigned int)(size + sizeof(uint32_t)), out + 8U);
|
||||
|
||||
delete[] in;
|
||||
|
||||
return write(out, 40U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeOptions()
|
||||
{
|
||||
char buffer[300U];
|
||||
|
||||
::memcpy(buffer + 0U, "RPTO", 4U);
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
::strcpy(buffer + 8U, m_options.c_str());
|
||||
|
||||
return write((unsigned char*)buffer, (unsigned int)m_options.length() + 8U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writeConfig()
|
||||
@@ -614,40 +395,13 @@ bool CDMRNetwork::writeConfig()
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[400U];
|
||||
char buffer[100U];
|
||||
|
||||
::memcpy(buffer + 0U, "RPTC", 4U);
|
||||
::memcpy(buffer + 0U, "DMRC", 4U);
|
||||
::memcpy(buffer + 4U, m_id, 4U);
|
||||
::sprintf(buffer + 8U, "%02u%c%-40.40s%-40.40s", m_colorCode, slots, m_version, software);
|
||||
|
||||
char latitude[20U];
|
||||
::sprintf(latitude, "%08f", m_latitude);
|
||||
|
||||
char longitude[20U];
|
||||
::sprintf(longitude, "%09f", m_longitude);
|
||||
|
||||
unsigned int power = m_power;
|
||||
if (power > 99U)
|
||||
power = 99U;
|
||||
|
||||
int height = m_height;
|
||||
if (height > 999)
|
||||
height = 999;
|
||||
|
||||
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
|
||||
m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(),
|
||||
m_description.c_str(), slots, m_url.c_str(), m_version, software);
|
||||
|
||||
return write((unsigned char*)buffer, 302U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::writePing()
|
||||
{
|
||||
unsigned char buffer[11U];
|
||||
|
||||
::memcpy(buffer + 0U, "RPTPING", 7U);
|
||||
::memcpy(buffer + 7U, m_id, 4U);
|
||||
|
||||
return write(buffer, 11U);
|
||||
return write((unsigned char*)buffer, 91U);
|
||||
}
|
||||
|
||||
bool CDMRNetwork::wantsBeacon()
|
||||
@@ -670,8 +424,6 @@ bool CDMRNetwork::write(const unsigned char* data, unsigned int length)
|
||||
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");
|
||||
m_socket.close();
|
||||
open();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user