drop dvmdfsi as a standalone application (this functionality has been entirely rolled into dvmhost); update README.md;
parent
617c889d1a
commit
ec1cf8c87e
@ -1,105 +0,0 @@
|
||||
#
|
||||
# Digital Voice Modem - DFSI Software Configuration
|
||||
#
|
||||
# @package DVM / DFSI Software
|
||||
#
|
||||
|
||||
# Flag indicating whether the host will run as a background or foreground task.
|
||||
daemon: false
|
||||
|
||||
#
|
||||
# Network Configuration
|
||||
#
|
||||
network:
|
||||
# Textual Name
|
||||
identity: DFSI
|
||||
# Network Peer ID
|
||||
peerId: 9000123
|
||||
# Hostname/IP address of FNE master to connect to.
|
||||
address: 127.0.0.1
|
||||
# Port number to connect to.
|
||||
port: 62031
|
||||
# FNE access password.
|
||||
password: RPT1234
|
||||
# Additional debug logging
|
||||
debug: false
|
||||
# Encrypted endpoint networking
|
||||
encrypted: false
|
||||
# Pre-shared key for encrypted networking (AES-256 32-byte hex keystring)
|
||||
presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
|
||||
|
||||
#
|
||||
# DFSI Configuration
|
||||
#
|
||||
dfsi:
|
||||
# Mode
|
||||
# 1 - UDP DFSI to DVM FNE (TODO: Implement this)
|
||||
# 2 - Serial DFSI to DVM FNE (Using DVM-V24 adapter board)
|
||||
# 3 - Serial DFSI to UDP DFSI (TODO: Implement this)
|
||||
mode: 2
|
||||
|
||||
# Flag enabling "the" Manufacturer standard of RTP.
|
||||
theManufacturer: false
|
||||
|
||||
# P25 buffer size in bytes (don't change this unless you have a good reason to)
|
||||
# Default of 3060 is 10 full LDU1/2s worth of data
|
||||
p25BufferSize: 3060
|
||||
|
||||
udp:
|
||||
# Time in seconds between heartbets to DFSI peers.
|
||||
heartbeat: 5
|
||||
# Flag disabling control connection establishment.
|
||||
noConnectionEstablishment: false
|
||||
# Local DFSI RTP Port number.
|
||||
localRtpPort: 27500
|
||||
# Remote RFSS DFSI Hostname/IP address of FNE master to connect to.
|
||||
remoteDfsiAddress: 127.0.0.2
|
||||
# Remote DFSI Control Port number to connect to.
|
||||
remoteControlPort: 27000
|
||||
# Remote DFSI RTP Port number to connect to.
|
||||
remoteRtpPort: 27500
|
||||
|
||||
serial:
|
||||
# Modem port type.
|
||||
portType: "uart" # Valid values are "null", and "uart"
|
||||
# Serial configuration for serial DFSI
|
||||
port: "/dev/ttyACM0"
|
||||
#
|
||||
baudrate: 115200
|
||||
# RT/RT flag enabled (0x02) or disabled (0x04)
|
||||
rtrt: true
|
||||
# Use the DIU source flag (0x00) instead of the quantar source flag (0x02)
|
||||
diu: true
|
||||
# Jitter buffer length in ms
|
||||
jitter: 200
|
||||
# Debug logging
|
||||
debug: true
|
||||
# Trace logging (prints lots of data)
|
||||
trace: false
|
||||
|
||||
# Timer which will reset local/remote call flags if frames aren't received longer than this time in ms
|
||||
callTimeout: 200
|
||||
|
||||
#
|
||||
# Logging Configuration
|
||||
# Logging Levels:
|
||||
# 1 - Debug
|
||||
# 2 - Message
|
||||
# 3 - Informational
|
||||
# 4 - Warning
|
||||
# 5 - Error
|
||||
# 6 - Fatal
|
||||
#
|
||||
log:
|
||||
# Console display logging level (used when in foreground).
|
||||
displayLevel: 1
|
||||
# File logging level.
|
||||
fileLevel: 1
|
||||
# Flag indicating file logs should be sent to syslog instead of a file.
|
||||
useSyslog: false
|
||||
# Full path for the directory to store the log files.
|
||||
filePath: .
|
||||
# Full path for the directory to store the activity log files.
|
||||
activityFilePath: .
|
||||
# Log filename prefix.
|
||||
fileRoot: dvmdfsi
|
||||
@ -1,150 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
#include "ActivityLog.h"
|
||||
#include "common/network/BaseNetwork.h"
|
||||
#include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork()
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#if defined(CATCH2_TEST_COMPILATION)
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define EOL "\r\n"
|
||||
|
||||
const uint32_t ACT_LOG_BUFFER_LEN = 501U;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Variables
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static std::string m_actFilePath;
|
||||
static std::string m_actFileRoot;
|
||||
|
||||
static FILE* m_actFpLog = nullptr;
|
||||
|
||||
static struct tm m_actTm;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Helper to open the activity log file, file handle. */
|
||||
|
||||
static bool ActivityLogOpen()
|
||||
{
|
||||
if (CurrentLogFileLevel() == 0U)
|
||||
return true;
|
||||
|
||||
time_t now;
|
||||
::time(&now);
|
||||
|
||||
struct tm* tm = ::localtime(&now);
|
||||
|
||||
if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) {
|
||||
if (m_actFpLog != nullptr)
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (m_actFpLog != nullptr)
|
||||
::fclose(m_actFpLog);
|
||||
}
|
||||
|
||||
char filename[200U];
|
||||
::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
||||
|
||||
m_actFpLog = ::fopen(filename, "a+t");
|
||||
m_actTm = *tm;
|
||||
|
||||
return m_actFpLog != nullptr;
|
||||
}
|
||||
|
||||
/* Initializes the activity log. */
|
||||
|
||||
bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot)
|
||||
{
|
||||
#if defined(CATCH2_TEST_COMPILATION)
|
||||
return true;
|
||||
#endif
|
||||
m_actFilePath = filePath;
|
||||
m_actFileRoot = fileRoot;
|
||||
|
||||
return ::ActivityLogOpen();
|
||||
}
|
||||
|
||||
/* Finalizes the activity log. */
|
||||
|
||||
void ActivityLogFinalise()
|
||||
{
|
||||
#if defined(CATCH2_TEST_COMPILATION)
|
||||
return;
|
||||
#endif
|
||||
if (m_actFpLog != nullptr)
|
||||
::fclose(m_actFpLog);
|
||||
}
|
||||
|
||||
/* Writes a new entry to the activity log. */
|
||||
|
||||
void ActivityLog(const char* msg, ...)
|
||||
{
|
||||
#if defined(CATCH2_TEST_COMPILATION)
|
||||
return;
|
||||
#endif
|
||||
assert(msg != nullptr);
|
||||
|
||||
char buffer[ACT_LOG_BUFFER_LEN];
|
||||
struct timeval now;
|
||||
::gettimeofday(&now, NULL);
|
||||
|
||||
struct tm* tm = ::gmtime(&now.tv_sec);
|
||||
|
||||
::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U);
|
||||
|
||||
va_list vl, vl_len;
|
||||
va_start(vl, msg);
|
||||
va_copy(vl_len, vl);
|
||||
|
||||
size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len);
|
||||
::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl);
|
||||
|
||||
va_end(vl_len);
|
||||
va_end(vl);
|
||||
|
||||
bool ret = ::ActivityLogOpen();
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
if (LogGetNetwork() != nullptr) {
|
||||
network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();;
|
||||
network->writeActLog(buffer);
|
||||
}
|
||||
|
||||
if (CurrentLogFileLevel() == 0U)
|
||||
return;
|
||||
|
||||
::fprintf(m_actFpLog, "%s\n", buffer);
|
||||
::fflush(m_actFpLog);
|
||||
|
||||
if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) {
|
||||
::fprintf(stdout, "%s" EOL, buffer);
|
||||
::fflush(stdout);
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file ActivityLog.h
|
||||
* @ingroup dfsi
|
||||
* @file ActivityLog.cpp
|
||||
* @ingroup dfsi
|
||||
*/
|
||||
#if !defined(__ACTIVITY_LOG_H__)
|
||||
#define __ACTIVITY_LOG_H__
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Initializes the activity log.
|
||||
* @param filePath File path for the log file.
|
||||
* @param fileRoot Root name for log file.
|
||||
*/
|
||||
extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot);
|
||||
/**
|
||||
* @brief Finalizes the activity log.
|
||||
*/
|
||||
extern HOST_SW_API void ActivityLogFinalise();
|
||||
/**
|
||||
* @brief Writes a new entry to the activity log.
|
||||
* @param msg String format.
|
||||
*
|
||||
* This is a variable argument function.
|
||||
*/
|
||||
extern HOST_SW_API void ActivityLog(const char* msg, ...);
|
||||
|
||||
#endif // __ACTIVITY_LOG_H__
|
||||
@ -1,23 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#/*
|
||||
# * Digital Voice Modem - Modem Host Software
|
||||
# * GPLv2 Open Source. Use is subject to license terms.
|
||||
# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
# *
|
||||
# * Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
# *
|
||||
# */
|
||||
file(GLOB dvmdfsi_SRC
|
||||
"src/host/modem/port/*.h"
|
||||
"src/host/modem/port/*.cpp"
|
||||
"src/host/network/Network.h"
|
||||
"src/host/network/Network.cpp"
|
||||
|
||||
# Network Core
|
||||
"src/dfsi/network/*.h"
|
||||
"src/dfsi/network/*.cpp"
|
||||
|
||||
# Core
|
||||
"src/dfsi/*.h"
|
||||
"src/dfsi/*.cpp"
|
||||
)
|
||||
@ -1,45 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @defgroup dfsi DFSI V.24/UDP Software (dvmdfsi)
|
||||
* @brief Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* @details TIA/V.24 standard interface application that connects to a V.24 interface board or UDP to allow for P25 DFSI communications with commercial P25 hardware.
|
||||
* @ingroup dfsi
|
||||
*
|
||||
* @file Defines.h
|
||||
* @ingroup dfsi
|
||||
*/
|
||||
#if !defined(__DEFINES_H__)
|
||||
#define __DEFINES_H__
|
||||
|
||||
#include "common/Defines.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#undef __PROG_NAME__
|
||||
#define __PROG_NAME__ "Digital Voice Modem (DVM) DFSI V.24/UDP Peer"
|
||||
#undef __EXE_NAME__
|
||||
#define __EXE_NAME__ "dvmdfsi"
|
||||
|
||||
#undef __NETVER__
|
||||
#define __NETVER__ "DFSI" VERSION_MAJOR VERSION_REV VERSION_MINOR
|
||||
|
||||
#undef DEFAULT_CONF_FILE
|
||||
#define DEFAULT_CONF_FILE "dfsi-config.yml"
|
||||
#undef DEFAULT_LOCK_FILE
|
||||
#define DEFAULT_LOCK_FILE "/tmp/dvmdfsi.lock"
|
||||
|
||||
#define DFSI_MODE_UDP_FNE 1
|
||||
#define DFSI_MODE_V24_FNE 2
|
||||
#define DFSI_MODE_UDP_V24 3
|
||||
|
||||
#endif // __DEFINES_H__
|
||||
@ -1,418 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/dmr/DMRDefines.h"
|
||||
#include "common/p25/P25Utils.h"
|
||||
#include "common/network/udp/Socket.h"
|
||||
#include "common/Log.h"
|
||||
#include "common/StopWatch.h"
|
||||
#include "common/Thread.h"
|
||||
#include "common/Utils.h"
|
||||
#include "host/ActivityLog.h"
|
||||
#include "Dfsi.h"
|
||||
#include "DfsiMain.h"
|
||||
|
||||
using namespace network;
|
||||
using namespace lookups;
|
||||
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define IDLE_WARMUP_MS 5U
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the HostTest class. */
|
||||
|
||||
Dfsi::Dfsi(const std::string& confFile) :
|
||||
m_confFile(confFile),
|
||||
m_conf(),
|
||||
m_network(nullptr),
|
||||
m_ridLookup(nullptr),
|
||||
m_tidLookup(nullptr),
|
||||
m_pingTime(5U),
|
||||
m_maxMissedPings(5U),
|
||||
m_updateLookupTime(10U),
|
||||
m_debug(false),
|
||||
m_repeatTraffic(true),
|
||||
m_serial(nullptr)
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
|
||||
/* Finalizes a instance of the HostTest class. */
|
||||
|
||||
Dfsi::~Dfsi() = default;
|
||||
|
||||
/* Executes the main FNE processing loop. */
|
||||
|
||||
int Dfsi::run()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// Try and parse the config yaml
|
||||
try {
|
||||
ret = yaml::Parse(m_conf, m_confFile.c_str());
|
||||
if (!ret) {
|
||||
::fatal("cannot read the configuration file, %s\n", m_confFile.c_str());
|
||||
}
|
||||
}
|
||||
catch (yaml::OperationException const& e) {
|
||||
::fatal("cannot read the configuration file - %s (%s)", m_confFile.c_str(), e.message());
|
||||
}
|
||||
|
||||
// Check if we should run as a daemn or not
|
||||
bool m_daemon = m_conf["daemon"].as<bool>(false);
|
||||
if (m_daemon && g_foreground)
|
||||
m_daemon = false;
|
||||
|
||||
// initialize system logging
|
||||
yaml::Node logConf = m_conf["log"];
|
||||
ret = ::LogInitialise(logConf["filePath"].as<std::string>(), logConf["fileRoot"].as<std::string>(),
|
||||
logConf["fileLevel"].as<uint32_t>(0U), logConf["displayLevel"].as<uint32_t>(0U), false, logConf["useSyslog"].as<bool>(false));
|
||||
if (!ret) {
|
||||
::fatal("unable to open the log file\n");
|
||||
}
|
||||
|
||||
// Init activity logging
|
||||
ret = ::ActivityLogInitialise(logConf["activityFilePath"].as<std::string>(), logConf["fileRoot"].as<std::string>());
|
||||
if (!ret) {
|
||||
::fatal("unable to open the activity log file\n");
|
||||
}
|
||||
|
||||
// handle POSIX process forking
|
||||
if (m_daemon) {
|
||||
// create new process
|
||||
pid_t pid = ::fork();
|
||||
if (pid == -1) {
|
||||
::fprintf(stderr, "%s: Couldn't fork() , exiting\n", g_progExe.c_str());
|
||||
::LogFinalise();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (pid != 0) {
|
||||
::LogFinalise();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// create new session and process group
|
||||
if (::setsid() == -1) {
|
||||
::fprintf(stderr, "%s: Couldn't setsid(), exiting\n", g_progExe.c_str());
|
||||
::LogFinalise();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// set the working directory to the root directory
|
||||
if (::chdir("/") == -1) {
|
||||
::fprintf(stderr, "%s: Couldn't cd /, exiting\n", g_progExe.c_str());
|
||||
::LogFinalise();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
::close(STDIN_FILENO);
|
||||
::close(STDOUT_FILENO);
|
||||
::close(STDERR_FILENO);
|
||||
}
|
||||
|
||||
::LogInfo(__BANNER__ "\r\n" __PROG_NAME__ " " __VER__ " (built " __BUILD__ ")\r\n" \
|
||||
"Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\r\n" \
|
||||
">> DFSI Network Peer\r\n");
|
||||
|
||||
// read base parameters from configuration
|
||||
ret = readParams();
|
||||
if (!ret)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Read DFSI config
|
||||
yaml::Node dfsi_conf = m_conf["dfsi"];
|
||||
uint16_t dfsiMode = dfsi_conf["mode"].as<uint16_t>();
|
||||
uint32_t p25BufferSize = dfsi_conf["p25BufferSize"].as<uint32_t>();
|
||||
uint16_t callTimeout = dfsi_conf["callTimeout"].as<uint16_t>();
|
||||
|
||||
// initialize peer networking
|
||||
ret = createPeerNetwork();
|
||||
if (!ret)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
std::string dfsiModeStr = "Unknown";
|
||||
|
||||
switch (dfsiMode) {
|
||||
case DFSI_MODE_UDP_FNE:
|
||||
{
|
||||
dfsiModeStr = "UDP DFSI to FNE";
|
||||
LogError(LOG_HOST, "UDP DFSI mode not yet supported, sorry!");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case DFSI_MODE_V24_FNE:
|
||||
{
|
||||
dfsiModeStr = "V24 DFSI to FNE";
|
||||
ret = createSerialNetwork(p25BufferSize, callTimeout);
|
||||
if (!ret)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case DFSI_MODE_UDP_V24:
|
||||
{
|
||||
dfsiModeStr = "UDP DFSI to V24 DFSI";
|
||||
LogError(LOG_HOST, "UDP to V24 mode not yet supported, sorry!");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
LogError(LOG_HOST, "Invalid DFSI mode specified: %d", dfsiMode);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
LogInfo("DFSI Parameters");
|
||||
LogInfo(" Mode: %u (%s)", dfsiMode, dfsiModeStr.c_str());
|
||||
LogInfo(" P25 Buffer Size: %u bytes", p25BufferSize);
|
||||
LogInfo(" Call Timeout: %u ms", callTimeout);
|
||||
|
||||
StopWatch stopWatch;
|
||||
stopWatch.start();
|
||||
|
||||
/*
|
||||
** Main execution loop
|
||||
*/
|
||||
|
||||
struct utsname utsinfo;
|
||||
::memset(&utsinfo, 0, sizeof(utsinfo));
|
||||
::uname(&utsinfo);
|
||||
|
||||
::LogInfoEx(LOG_HOST, "[ OK ] DFSI is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine);
|
||||
|
||||
// Get serial board info if connected
|
||||
if (m_serial)
|
||||
{
|
||||
m_serial->getBoardInfo();
|
||||
}
|
||||
|
||||
// main execution loop
|
||||
while (!g_killed) {
|
||||
uint32_t ms = stopWatch.elapsed();
|
||||
|
||||
ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
// ------------------------------------------------------
|
||||
// -- Network RX Clocking --
|
||||
// ------------------------------------------------------
|
||||
|
||||
if (m_network != nullptr)
|
||||
m_network->clock(ms);
|
||||
|
||||
uint32_t length = 0U;
|
||||
bool netReadRet = false;
|
||||
|
||||
UInt8Array p25Buffer = m_network->readP25(netReadRet, length);
|
||||
if (netReadRet) {
|
||||
// Send the data to the serial handler if serial is up
|
||||
if (m_serial != nullptr)
|
||||
m_serial->processP25FromNet(std::move(p25Buffer), length);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// -- Network TX Clocking --
|
||||
// ------------------------------------------------------
|
||||
|
||||
// Processes data in the serial rx P25 buffer and sends it to the network buffer for sending, if serial is up
|
||||
if (m_serial != nullptr) {
|
||||
m_serial->processP25ToNet();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// -- Serial Clocking --
|
||||
// ------------------------------------------------------
|
||||
|
||||
if (m_serial != nullptr) {
|
||||
m_serial->clock(ms);
|
||||
}
|
||||
|
||||
// Timekeeping
|
||||
if (ms < 2U)
|
||||
Thread::sleep(1U);
|
||||
}
|
||||
|
||||
::LogSetNetwork(nullptr);
|
||||
if (m_network != nullptr) {
|
||||
m_network->close();
|
||||
delete m_network;
|
||||
}
|
||||
|
||||
if (m_serial != nullptr) {
|
||||
m_serial->close();
|
||||
delete m_serial;
|
||||
}
|
||||
|
||||
if (m_tidLookup != nullptr) {
|
||||
m_tidLookup->stop();
|
||||
delete m_tidLookup;
|
||||
}
|
||||
if (m_ridLookup != nullptr) {
|
||||
m_ridLookup->stop();
|
||||
delete m_ridLookup;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Reads basic configuration parameters from the YAML configuration file. */
|
||||
|
||||
bool Dfsi::readParams()
|
||||
{
|
||||
// No basic config params right now
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Initializes peer network connectivity. */
|
||||
|
||||
bool Dfsi::createPeerNetwork()
|
||||
{
|
||||
yaml::Node networkConf = m_conf["network"];
|
||||
std::string password = networkConf["password"].as<std::string>();
|
||||
|
||||
std::string address = networkConf["address"].as<std::string>();
|
||||
uint16_t port = networkConf["port"].as<uint16_t>();
|
||||
uint32_t id = networkConf["peerId"].as<uint32_t>();
|
||||
|
||||
bool encrypted = networkConf["encrypted"].as<bool>(false);
|
||||
std::string key = networkConf["presharedKey"].as<std::string>();
|
||||
uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
|
||||
if (!key.empty()) {
|
||||
if (key.size() == 32) {
|
||||
// bryanb: shhhhhhh....dirty nasty hacks
|
||||
key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs)
|
||||
LogWarning(LOG_HOST, "Half-length network preshared encryption key detected, doubling key on itself.");
|
||||
}
|
||||
|
||||
if (key.size() == 64) {
|
||||
if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
|
||||
const char* keyPtr = key.c_str();
|
||||
::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
|
||||
|
||||
for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) {
|
||||
char t[4] = {keyPtr[0], keyPtr[1], 0};
|
||||
presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16);
|
||||
keyPtr += 2 * sizeof(char);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogWarning(LOG_HOST, "Invalid characters in the network preshared encryption key. Encryption disabled.");
|
||||
encrypted = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogWarning(LOG_HOST, "Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled.");
|
||||
encrypted = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string identity = networkConf["identity"].as<std::string>();
|
||||
|
||||
bool netDebug = networkConf["debug"].as<bool>();
|
||||
|
||||
LogInfo("Network Parameters");
|
||||
LogInfo(" Identity: %s", identity.c_str());
|
||||
LogInfo(" Peer ID: %u", id);
|
||||
LogInfo(" Address: %s", address.c_str());
|
||||
LogInfo(" Port: %u", port);
|
||||
LogInfo(" Encrypted: %s", encrypted ? "yes" : "no");
|
||||
|
||||
if (id > 999999999U) {
|
||||
::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize networking
|
||||
m_network = new DfsiPeerNetwork(address, port, 0U, id, password, true, netDebug, false, true, false, true, true, true, true, true, false);
|
||||
m_network->setMetadata(identity, 0U, 0U, 0.0F, 0.0F, 0, 0, 0, 0.0F, 0.0F, 0, "");
|
||||
m_network->setLookups(m_ridLookup, m_tidLookup);
|
||||
|
||||
::LogSetNetwork(m_network);
|
||||
|
||||
if (encrypted) {
|
||||
m_network->setPresharedKey(presharedKey);
|
||||
}
|
||||
|
||||
m_network->enable(true);
|
||||
bool ret = m_network->open();
|
||||
if (!ret) {
|
||||
delete m_network;
|
||||
m_network = nullptr;
|
||||
LogError(LOG_HOST, "failed to initialize traffic networking for PEER %u", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
::LogSetNetwork(m_network);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Initializes serial V.24 network. */
|
||||
|
||||
bool Dfsi::createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout)
|
||||
{
|
||||
// Read serial config
|
||||
yaml::Node dfsi_conf = m_conf["dfsi"];
|
||||
yaml::Node serial_conf = dfsi_conf["serial"];
|
||||
std::string portType = serial_conf["portType"].as<std::string>("null");
|
||||
std::string port = serial_conf["port"].as<std::string>();
|
||||
uint32_t baudrate = serial_conf["baudrate"].as<uint32_t>();
|
||||
bool rtrt = serial_conf["rtrt"].as<bool>();
|
||||
bool diu = serial_conf["diu"].as<bool>();
|
||||
uint16_t jitter = serial_conf["jitter"].as<uint16_t>();
|
||||
bool serial_debug = serial_conf["debug"].as<bool>();
|
||||
bool serial_trace = serial_conf["trace"].as<bool>();
|
||||
|
||||
LogInfo("Serial Parameters");
|
||||
LogInfo(" Port Type: %s", portType.c_str());
|
||||
LogInfo(" Port: %s", port.c_str());
|
||||
LogInfo(" Baudrate: %u", baudrate);
|
||||
LogInfo(" RT/RT: %s", rtrt ? "Enabled" : "Disabled");
|
||||
LogInfo(" DIU Flag: %s", diu ? "Enabled" : "Disabled");
|
||||
LogInfo(" Jitter Size: %u ms", jitter);
|
||||
LogInfo(" Debug: %s", serial_debug ? "Enabled" : "Disabled");
|
||||
LogInfo(" Trace: %s", serial_trace ? "Enabled" : "Disabled");
|
||||
|
||||
// Create serial service
|
||||
m_serial = new SerialService(portType, port, baudrate, rtrt, diu, jitter, m_network, p25BufferSize, p25BufferSize, callTimeout, serial_debug, serial_trace);
|
||||
|
||||
// Open serial
|
||||
bool ret = m_serial->open();
|
||||
if (!ret) {
|
||||
delete m_serial;
|
||||
m_serial = nullptr;
|
||||
LogError(LOG_HOST, "failed to initialied serial V24 interface");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file Dfsi.h
|
||||
* @ingroup dfsi
|
||||
* @file Dfsi.cpp
|
||||
* @ingroup dfsi
|
||||
*/
|
||||
#if !defined(__DFSI_H__)
|
||||
#define __DFSI_H__
|
||||
|
||||
#include "Defines.h"
|
||||
#include "common/lookups/RadioIdLookup.h"
|
||||
#include "common/lookups/TalkgroupRulesLookup.h"
|
||||
#include "common/yaml/Yaml.h"
|
||||
#include "common/Timer.h"
|
||||
#include "network/DfsiPeerNetwork.h"
|
||||
#include "network/SerialService.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief This class implements the core service logic.
|
||||
* @ingroup dfsi
|
||||
*/
|
||||
class HOST_SW_API Dfsi {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the HostTest class.
|
||||
* @param confFile Full-path to the configuration file.
|
||||
*/
|
||||
Dfsi(const std::string& confFile);
|
||||
/**
|
||||
* @brief Finalizes a instance of the HostTest class.
|
||||
*/
|
||||
~Dfsi();
|
||||
|
||||
/**
|
||||
* @brief Executes the main host processing loop.
|
||||
* @returns int Zero if successful, otherwise error occurred.
|
||||
*/
|
||||
int run();
|
||||
|
||||
private:
|
||||
const std::string& m_confFile;
|
||||
yaml::Node m_conf;
|
||||
|
||||
network::DfsiPeerNetwork* m_network;
|
||||
|
||||
lookups::RadioIdLookup* m_ridLookup;
|
||||
lookups::TalkgroupRulesLookup* m_tidLookup;
|
||||
|
||||
uint32_t m_pingTime;
|
||||
uint32_t m_maxMissedPings;
|
||||
|
||||
uint32_t m_updateLookupTime;
|
||||
|
||||
bool m_debug;
|
||||
|
||||
bool m_repeatTraffic;
|
||||
|
||||
network::SerialService* m_serial;
|
||||
|
||||
/**
|
||||
* @brief Reads basic configuration parameters from the INI.
|
||||
* @returns bool True, if configuration was read successfully, otherwise false.
|
||||
*/
|
||||
bool readParams();
|
||||
/**
|
||||
* @brief Initializes peer network connectivity.
|
||||
* @returns bool True, if network connectivity was initialized, otherwise false.
|
||||
*/
|
||||
bool createPeerNetwork();
|
||||
/**
|
||||
* @brief Initializes serial V.24 network.
|
||||
* @returns bool True, if serial connectivity was initialized, otherwise false.
|
||||
*/
|
||||
bool createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout);
|
||||
};
|
||||
|
||||
#endif // __DFSI_H__
|
||||
@ -1,230 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "Defines.h"
|
||||
#include "common/Log.h"
|
||||
#include "dfsi/ActivityLog.h"
|
||||
#include "DfsiMain.h"
|
||||
#include "Dfsi.h"
|
||||
|
||||
using namespace network;
|
||||
using namespace lookups;
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <vector>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macros
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define IS(s) (::strcmp(argv[i], s) == 0)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Variables
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int g_signal = 0;
|
||||
std::string g_progExe = std::string(__EXE_NAME__);
|
||||
std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
|
||||
std::string g_lockFile = std::string(DEFAULT_LOCK_FILE);
|
||||
|
||||
std::string g_masterAddress = std::string("127.0.0.1");
|
||||
uint16_t g_masterPort = 63031;
|
||||
uint32_t g_peerId = 9000999;
|
||||
|
||||
bool g_foreground = false;
|
||||
bool g_killed = false;
|
||||
bool g_hideMessages = false;
|
||||
|
||||
uint8_t* g_gitHashBytes = nullptr;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(CATCH2_TEST_COMPILATION)
|
||||
/* Internal signal handler. */
|
||||
|
||||
static void sigHandler(int signum)
|
||||
{
|
||||
g_signal = signum;
|
||||
g_killed = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Helper to print a fatal error message and exit. */
|
||||
|
||||
void fatal(const char* msg, ...)
|
||||
{
|
||||
char buffer[400U];
|
||||
::memset(buffer, 0x20U, 400U);
|
||||
|
||||
va_list vl;
|
||||
va_start(vl, msg);
|
||||
|
||||
::vsprintf(buffer, msg, vl);
|
||||
|
||||
va_end(vl);
|
||||
|
||||
::fprintf(stderr, "%s: %s\n", g_progExe.c_str(), buffer);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Helper to pring usage the command line arguments. (And optionally an error.) */
|
||||
|
||||
void usage(const char* message, const char* arg)
|
||||
{
|
||||
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
|
||||
::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n\n");
|
||||
if (message != nullptr) {
|
||||
::fprintf(stderr, "%s: ", g_progExe.c_str());
|
||||
::fprintf(stderr, message, arg);
|
||||
::fprintf(stderr, "\n\n");
|
||||
}
|
||||
|
||||
::fprintf(stdout,
|
||||
"usage: %s [-vhf]"
|
||||
"[--syslog]"
|
||||
"[-c <configuration file>]"
|
||||
"\n\n"
|
||||
" -v show version information\n"
|
||||
" -h show this screen\n"
|
||||
" -f foreground mode\n"
|
||||
"\n"
|
||||
" --syslog force logging to syslog\n"
|
||||
"\n"
|
||||
" -c <file> specifies the configuration file to use\n"
|
||||
"\n"
|
||||
" -- stop handling options\n",
|
||||
g_progExe.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Helper to validate the command line arguments. */
|
||||
|
||||
int checkArgs(int argc, char* argv[])
|
||||
{
|
||||
int i, p = 0;
|
||||
|
||||
// iterate through arguments
|
||||
for (i = 1; i <= argc; i++)
|
||||
{
|
||||
if (argv[i] == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*argv[i] != '-') {
|
||||
continue;
|
||||
}
|
||||
else if (IS("--")) {
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
else if (IS("-f")) {
|
||||
g_foreground = true;
|
||||
}
|
||||
else if (IS("--syslog")) {
|
||||
g_useSyslog = true;
|
||||
}
|
||||
else if (IS("-c")) {
|
||||
if (argc-- <= 0)
|
||||
usage("error: %s", "must specify the configuration file to use");
|
||||
g_iniFile = std::string(argv[++i]);
|
||||
|
||||
if (g_iniFile.empty())
|
||||
usage("error: %s", "configuration file cannot be blank!");
|
||||
|
||||
p += 2;
|
||||
}
|
||||
else if (IS("-v")) {
|
||||
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
|
||||
::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n");
|
||||
::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n");
|
||||
if (argc == 2)
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (IS("-h")) {
|
||||
usage(nullptr, nullptr);
|
||||
if (argc == 2)
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else {
|
||||
usage("unrecognized option `%s'", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (p < 0 || p > argc) {
|
||||
p = 0;
|
||||
}
|
||||
|
||||
return ++p;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Program Entry Point
|
||||
// ---------------------------------------------------------------------------
|
||||
#if !defined(CATCH2_TEST_COMPILATION)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
g_gitHashBytes = new uint8_t[4U];
|
||||
::memset(g_gitHashBytes, 0x00U, 4U);
|
||||
|
||||
uint32_t hash = ::strtoul(__GIT_VER_HASH__, 0, 16);
|
||||
__SET_UINT32(hash, g_gitHashBytes, 0U);
|
||||
|
||||
if (argv[0] != nullptr && *argv[0] != 0)
|
||||
g_progExe = std::string(argv[0]);
|
||||
|
||||
if (argc > 1) {
|
||||
// check arguments
|
||||
int i = checkArgs(argc, argv);
|
||||
if (i < argc) {
|
||||
argc -= i;
|
||||
argv += i;
|
||||
}
|
||||
else {
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
}
|
||||
|
||||
::signal(SIGINT, sigHandler);
|
||||
::signal(SIGTERM, sigHandler);
|
||||
::signal(SIGHUP, sigHandler);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
g_signal = 0;
|
||||
g_killed = false;
|
||||
|
||||
Dfsi *dfsi = new Dfsi(g_iniFile);
|
||||
ret = dfsi->run();
|
||||
delete dfsi;
|
||||
|
||||
if (g_signal == 2)
|
||||
::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGINT");
|
||||
|
||||
if (g_signal == 15)
|
||||
::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGTERM");
|
||||
|
||||
if (g_signal == 1)
|
||||
::LogInfoEx(LOG_HOST, "[RSTR] dvmdfsi:main SIGHUP");
|
||||
} while (g_signal == 1);
|
||||
|
||||
::LogFinalise();
|
||||
::ActivityLogFinalise();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@ -1,60 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file DfsiMain.h
|
||||
* @ingroup dfsi
|
||||
* @file DfsiMain.cpp
|
||||
* @ingroup dfsi
|
||||
*/
|
||||
#if !defined(__DFSI_MAIN_H__)
|
||||
#define __DFSI_MAIN_H__
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Externs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** @brief */
|
||||
extern int g_signal;
|
||||
/** @brief */
|
||||
extern std::string g_progExe;
|
||||
/** @brief */
|
||||
extern std::string g_iniFile;
|
||||
/** @brief */
|
||||
extern std::string g_lockFile;
|
||||
|
||||
/** @brief (Global) Flag indicating foreground operation. */
|
||||
extern bool g_foreground;
|
||||
/** @brief (Global) Flag indicating the FNE should stop immediately. */
|
||||
extern bool g_killed;
|
||||
|
||||
/** @brief */
|
||||
extern std::string g_masterAddress;
|
||||
/** @brief */
|
||||
extern uint16_t g_masterPort;
|
||||
/** @brief */
|
||||
extern uint32_t g_peerId;
|
||||
|
||||
extern uint8_t* g_gitHashBytes;
|
||||
|
||||
/**
|
||||
* @brief Helper to trigger a fatal error message. This will cause the program to terminate
|
||||
* immediately with an error message.
|
||||
*
|
||||
* @param msg String format.
|
||||
*
|
||||
* This is a variable argument function.
|
||||
*/
|
||||
extern HOST_SW_API void fatal(const char* msg, ...);
|
||||
|
||||
#endif // __DFSI_MAIN_H__
|
||||
@ -1,99 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
#include "CallData.h"
|
||||
|
||||
using namespace network;
|
||||
using namespace modem;
|
||||
using namespace p25;
|
||||
using namespace p25::defines;
|
||||
using namespace p25::dfsi::frames;
|
||||
using namespace dfsi;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the VoiceCallData class. */
|
||||
|
||||
VoiceCallData::VoiceCallData() :
|
||||
srcId(0U),
|
||||
dstId(0U),
|
||||
lco(0U),
|
||||
mfId(MFG_STANDARD),
|
||||
serviceOptions(0U),
|
||||
lsd1(0U),
|
||||
lsd2(0U),
|
||||
mi(),
|
||||
algoId(ALGO_UNENCRYPT),
|
||||
kId(0U),
|
||||
VHDR1(),
|
||||
VHDR2(),
|
||||
netLDU1(),
|
||||
netLDU2(),
|
||||
seqNo(0U),
|
||||
n(0U),
|
||||
streamId(0U)
|
||||
{
|
||||
mi = new uint8_t[MI_LENGTH_BYTES];
|
||||
VHDR1 = new uint8_t[MotVoiceHeader1::HCW_LENGTH];
|
||||
VHDR2 = new uint8_t[MotVoiceHeader2::HCW_LENGTH];
|
||||
netLDU1 = new uint8_t[9U * 25U];
|
||||
netLDU2 = new uint8_t[9U * 25U];
|
||||
|
||||
::memset(netLDU1, 0x00U, 9U * 25U);
|
||||
::memset(netLDU2, 0x00U, 9U * 25U);
|
||||
}
|
||||
|
||||
/* Finalizes a instance of the VoiceCallData class. */
|
||||
|
||||
VoiceCallData::~VoiceCallData()
|
||||
{
|
||||
delete[] mi;
|
||||
delete[] VHDR1;
|
||||
delete[] VHDR2;
|
||||
delete[] netLDU1;
|
||||
delete[] netLDU2;
|
||||
}
|
||||
|
||||
/* Reset call data to defaults. */
|
||||
|
||||
void VoiceCallData::resetCallData()
|
||||
{
|
||||
srcId = 0U;
|
||||
dstId = 0U;
|
||||
lco = 0U;
|
||||
mfId = MFG_STANDARD;
|
||||
serviceOptions = 0U;
|
||||
lsd1 = 0U;
|
||||
lsd2 = 0U;
|
||||
|
||||
::memset(mi, 0x00U, MI_LENGTH_BYTES);
|
||||
|
||||
algoId = ALGO_UNENCRYPT;
|
||||
kId = 0U;
|
||||
|
||||
::memset(VHDR1, 0x00U, MotVoiceHeader1::HCW_LENGTH);
|
||||
::memset(VHDR2, 0x00U, MotVoiceHeader2::HCW_LENGTH);
|
||||
|
||||
::memset(netLDU1, 0x00U, 9U * 25U);
|
||||
::memset(netLDU2, 0x00U, 9U * 25U);
|
||||
|
||||
n = 0U;
|
||||
seqNo = 0U;
|
||||
streamId = 0U;
|
||||
}
|
||||
|
||||
/* Generate a new stream ID for a call. */
|
||||
|
||||
void VoiceCallData::newStreamId()
|
||||
{
|
||||
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
|
||||
streamId = dist(random);
|
||||
}
|
||||
@ -1,153 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file CallData.h
|
||||
* @ingroup dfsi_network
|
||||
* @file CallData.cpp
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
#if !defined(__DFSI_CALL_DATA_H__)
|
||||
#define __DFSI_CALL_DATA_H__
|
||||
|
||||
#include "Defines.h"
|
||||
#include "common/edac/RS634717.h"
|
||||
#include "common/network/RawFrameQueue.h"
|
||||
#include "common/p25/data/LowSpeedData.h"
|
||||
#include "common/p25/P25Defines.h"
|
||||
#include "common/p25/dfsi/DFSIDefines.h"
|
||||
#include "common/p25/dfsi/LC.h"
|
||||
#include "common/p25/dfsi/frames/Frames.h"
|
||||
#include "common/Log.h"
|
||||
#include "common/Utils.h"
|
||||
#include "common/yaml/Yaml.h"
|
||||
#include "common/RingBuffer.h"
|
||||
#include "network/DfsiPeerNetwork.h"
|
||||
#include "host/modem/Modem.h"
|
||||
#include "host/modem/port/IModemPort.h"
|
||||
#include "host/modem/port/UARTPort.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace network
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Represents an on-going call.
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
class HOST_SW_API VoiceCallData {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the VoiceCallData class.
|
||||
*/
|
||||
VoiceCallData();
|
||||
/**
|
||||
* @brief Initializes a new instance of the VoiceCallData class.
|
||||
*/
|
||||
~VoiceCallData();
|
||||
|
||||
/**
|
||||
* @brief Reset call data to defaults.
|
||||
*/
|
||||
void resetCallData();
|
||||
/**
|
||||
* @brief Generate a new stream ID for a call.
|
||||
*/
|
||||
void newStreamId();
|
||||
|
||||
/** @name Call Data */
|
||||
/**
|
||||
* @brief Source Radio ID.
|
||||
*/
|
||||
uint32_t srcId;
|
||||
/**
|
||||
* @brief Destination ID.
|
||||
*/
|
||||
uint32_t dstId;
|
||||
|
||||
/**
|
||||
* @brief Link Control Opcode.
|
||||
*/
|
||||
uint8_t lco;
|
||||
/**
|
||||
* @brief Manufacturer ID.
|
||||
*/
|
||||
uint8_t mfId;
|
||||
/**
|
||||
* @brief Call Service Options.
|
||||
*/
|
||||
uint8_t serviceOptions;
|
||||
|
||||
/**
|
||||
* @brief Low Speed Data 1.
|
||||
*/
|
||||
uint8_t lsd1;
|
||||
/**
|
||||
* @brief Low Speed Data 2.
|
||||
*/
|
||||
uint8_t lsd2;
|
||||
|
||||
/**
|
||||
* @brief Encryption Message Indicator.
|
||||
*/
|
||||
uint8_t* mi;
|
||||
/**
|
||||
* @brief Encryption Algorithm ID.
|
||||
*/
|
||||
uint8_t algoId;
|
||||
/**
|
||||
* @brief Encryption Key ID.
|
||||
*/
|
||||
uint32_t kId;
|
||||
|
||||
/**
|
||||
* @brief Voice Header 1.
|
||||
*/
|
||||
uint8_t* VHDR1;
|
||||
/**
|
||||
* @brief Voice Header 2.
|
||||
*/
|
||||
uint8_t* VHDR2;
|
||||
|
||||
/**
|
||||
* @brief FNE Network LDU1 Buffer.
|
||||
*/
|
||||
uint8_t* netLDU1;
|
||||
/**
|
||||
* @brief FNE Network LDU2 Buffer.
|
||||
*/
|
||||
uint8_t* netLDU2;
|
||||
|
||||
/**
|
||||
* @brief Sequence Number.
|
||||
*/
|
||||
uint32_t seqNo;
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
uint8_t n;
|
||||
|
||||
/**
|
||||
* @brief Stream ID.
|
||||
*/
|
||||
uint32_t streamId;
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
// Used for stream ID generation
|
||||
std::mt19937 random;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __DFSI_CALL_DATA_H__
|
||||
@ -1,296 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "dfsi/Defines.h"
|
||||
#include "common/network/json/json.h"
|
||||
#include "common/p25/dfsi/DFSIDefines.h"
|
||||
#include "common/p25/dfsi/LC.h"
|
||||
#include "common/Utils.h"
|
||||
#include "network/DfsiPeerNetwork.h"
|
||||
|
||||
using namespace network;
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the PeerNetwork class. */
|
||||
|
||||
DfsiPeerNetwork::DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password,
|
||||
bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) :
|
||||
Network(address, port, localPort, peerId, password, duplex, debug, dmr, p25, nxdn, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup)
|
||||
{
|
||||
assert(!address.empty());
|
||||
assert(port > 0U);
|
||||
assert(!password.empty());
|
||||
}
|
||||
|
||||
/* Writes P25 LDU1 frame data to the network. */
|
||||
|
||||
bool DfsiPeerNetwork::writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, P25DEF::FrameType::E frameType)
|
||||
{
|
||||
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
|
||||
return false;
|
||||
|
||||
bool resetSeq = false;
|
||||
if (m_p25StreamId == 0U) {
|
||||
resetSeq = true;
|
||||
m_p25StreamId = createStreamId();
|
||||
}
|
||||
|
||||
uint32_t messageLength = 0U;
|
||||
UInt8Array message = createP25_LDU1Message_Raw(messageLength, control, lsd, data, frameType);
|
||||
if (message == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
|
||||
}
|
||||
|
||||
/* Writes P25 LDU2 frame data to the network. */
|
||||
|
||||
bool DfsiPeerNetwork::writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data)
|
||||
{
|
||||
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
|
||||
return false;
|
||||
|
||||
bool resetSeq = false;
|
||||
if (m_p25StreamId == 0U) {
|
||||
resetSeq = true;
|
||||
m_p25StreamId = createStreamId();
|
||||
}
|
||||
|
||||
uint32_t messageLength = 0U;
|
||||
UInt8Array message = createP25_LDU2Message_Raw(messageLength, control, lsd, data);
|
||||
if (message == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Protected Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Writes configuration to the network. */
|
||||
|
||||
bool DfsiPeerNetwork::writeConfig()
|
||||
{
|
||||
if (m_loginStreamId == 0U) {
|
||||
LogWarning(LOG_NET, "BUGBUG: tried to write network authorisation with no stream ID?");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* software = __NETVER__;
|
||||
|
||||
json::object config = json::object();
|
||||
|
||||
// identity and frequency
|
||||
config["identity"].set<std::string>(m_identity); // Identity
|
||||
config["rxFrequency"].set<uint32_t>(m_rxFrequency); // Rx Frequency
|
||||
config["txFrequency"].set<uint32_t>(m_txFrequency); // Tx Frequency
|
||||
|
||||
// system info
|
||||
json::object sysInfo = json::object();
|
||||
sysInfo["latitude"].set<float>(m_latitude); // Latitude
|
||||
sysInfo["longitude"].set<float>(m_longitude); // Longitude
|
||||
|
||||
sysInfo["height"].set<int>(m_height); // Height
|
||||
sysInfo["location"].set<std::string>(m_location); // Location
|
||||
config["info"].set<json::object>(sysInfo);
|
||||
|
||||
// channel data
|
||||
json::object channel = json::object();
|
||||
channel["txPower"].set<uint32_t>(m_power); // Tx Power
|
||||
channel["txOffsetMhz"].set<float>(m_txOffsetMhz); // Tx Offset (Mhz)
|
||||
channel["chBandwidthKhz"].set<float>(m_chBandwidthKhz); // Ch. Bandwidth (khz)
|
||||
channel["channelId"].set<uint8_t>(m_channelId); // Channel ID
|
||||
channel["channelNo"].set<uint32_t>(m_channelNo); // Channel No
|
||||
config["channel"].set<json::object>(channel);
|
||||
|
||||
// RCON
|
||||
json::object rcon = json::object();
|
||||
rcon["password"].set<std::string>(m_restApiPassword); // REST API Password
|
||||
rcon["port"].set<uint16_t>(m_restApiPort); // REST API Port
|
||||
config["rcon"].set<json::object>(rcon);
|
||||
|
||||
config["software"].set<std::string>(std::string(software)); // Software ID
|
||||
|
||||
json::value v = json::value(config);
|
||||
std::string json = v.serialize();
|
||||
|
||||
char buffer[json.length() + 8U];
|
||||
|
||||
::memcpy(buffer + 0U, TAG_REPEATER_CONFIG, 4U);
|
||||
::sprintf(buffer + 8U, "%s", json.c_str());
|
||||
|
||||
if (m_debug) {
|
||||
Utils::dump(1U, "Network Message, Configuration", (uint8_t*)buffer, json.length() + 8U);
|
||||
}
|
||||
|
||||
return writeMaster({ NET_FUNC::RPTC, NET_SUBFUNC::NOP }, (uint8_t*)buffer, json.length() + 8U, pktSeq(), m_loginStreamId);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Creates an P25 LDU1 frame message. */
|
||||
|
||||
UInt8Array DfsiPeerNetwork::createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||
const uint8_t* data, P25DEF::FrameType::E frameType)
|
||||
{
|
||||
using namespace p25::dfsi::defines;
|
||||
using namespace p25::defines;
|
||||
assert(data != nullptr);
|
||||
|
||||
p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd);
|
||||
|
||||
uint8_t* buffer = new uint8_t[P25_LDU1_PACKET_LENGTH + PACKET_PAD];
|
||||
::memset(buffer, 0x00U, P25_LDU1_PACKET_LENGTH + PACKET_PAD);
|
||||
|
||||
// construct P25 message header
|
||||
createP25_MessageHdr(buffer, DUID::LDU1, control, lsd, frameType);
|
||||
|
||||
// pack DFSI data
|
||||
uint32_t count = MSG_HDR_SIZE;
|
||||
uint8_t imbe[RAW_IMBE_LENGTH_BYTES];
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE1);
|
||||
::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 24U, imbe);
|
||||
count += DFSI_LDU1_VOICE1_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE2);
|
||||
::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 46U, imbe);
|
||||
count += DFSI_LDU1_VOICE2_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE3);
|
||||
::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 60U, imbe);
|
||||
count += DFSI_LDU1_VOICE3_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE4);
|
||||
::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 77U, imbe);
|
||||
count += DFSI_LDU1_VOICE4_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE5);
|
||||
::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 94U, imbe);
|
||||
count += DFSI_LDU1_VOICE5_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE6);
|
||||
::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 111U, imbe);
|
||||
count += DFSI_LDU1_VOICE6_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE7);
|
||||
::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 128U, imbe);
|
||||
count += DFSI_LDU1_VOICE7_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE8);
|
||||
::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 145U, imbe);
|
||||
count += DFSI_LDU1_VOICE8_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE9);
|
||||
::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU1(buffer + 162U, imbe);
|
||||
count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES;
|
||||
|
||||
buffer[23U] = count;
|
||||
|
||||
if (m_debug)
|
||||
Utils::dump(1U, "Network Message, P25 LDU1", buffer, (P25_LDU1_PACKET_LENGTH + PACKET_PAD));
|
||||
|
||||
length = (P25_LDU1_PACKET_LENGTH + PACKET_PAD);
|
||||
return UInt8Array(buffer);
|
||||
}
|
||||
|
||||
/* Creates an P25 LDU2 frame message. */
|
||||
|
||||
UInt8Array DfsiPeerNetwork::createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||
const uint8_t* data)
|
||||
{
|
||||
using namespace p25::dfsi::defines;
|
||||
using namespace p25::defines;
|
||||
assert(data != nullptr);
|
||||
|
||||
p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd);
|
||||
|
||||
uint8_t* buffer = new uint8_t[P25_LDU2_PACKET_LENGTH + PACKET_PAD];
|
||||
::memset(buffer, 0x00U, P25_LDU2_PACKET_LENGTH + PACKET_PAD);
|
||||
|
||||
// construct P25 message header
|
||||
createP25_MessageHdr(buffer, DUID::LDU2, control, lsd, FrameType::DATA_UNIT);
|
||||
|
||||
// pack DFSI data
|
||||
uint32_t count = MSG_HDR_SIZE;
|
||||
uint8_t imbe[RAW_IMBE_LENGTH_BYTES];
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE10);
|
||||
::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 24U, imbe);
|
||||
count += DFSI_LDU2_VOICE10_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE11);
|
||||
::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 46U, imbe);
|
||||
count += DFSI_LDU2_VOICE11_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE12);
|
||||
::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 60U, imbe);
|
||||
count += DFSI_LDU2_VOICE12_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE13);
|
||||
::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 77U, imbe);
|
||||
count += DFSI_LDU2_VOICE13_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE14);
|
||||
::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 94U, imbe);
|
||||
count += DFSI_LDU2_VOICE14_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE15);
|
||||
::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 111U, imbe);
|
||||
count += DFSI_LDU2_VOICE15_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE16);
|
||||
::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 128U, imbe);
|
||||
count += DFSI_LDU2_VOICE16_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE17);
|
||||
::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 145U, imbe);
|
||||
count += DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES;
|
||||
|
||||
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE18);
|
||||
::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES);
|
||||
dfsiLC.encodeLDU2(buffer + 162U, imbe);
|
||||
count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES;
|
||||
|
||||
buffer[23U] = count;
|
||||
|
||||
if (m_debug)
|
||||
Utils::dump(1U, "Network Message, P25 LDU2", buffer, (P25_LDU2_PACKET_LENGTH + PACKET_PAD));
|
||||
|
||||
length = (P25_LDU2_PACKET_LENGTH + PACKET_PAD);
|
||||
return UInt8Array(buffer);
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file DfsiPeerNetwork.h
|
||||
* @ingroup dfsi_network
|
||||
* @file DfsiPeerNetwork.cpp
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
#if !defined(__DFSI_PEER_NETWORK_H__)
|
||||
#define __DFSI_PEER_NETWORK_H__
|
||||
|
||||
#include "Defines.h"
|
||||
#include "host/network/Network.h"
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace network
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Implements the core peer networking logic.
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
class HOST_SW_API DfsiPeerNetwork : public Network {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the PeerNetwork class.
|
||||
* @param address Network Hostname/IP address to connect to.
|
||||
* @param port Network port number.
|
||||
* @param local
|
||||
* @param peerId Unique ID on the network.
|
||||
* @param password Network authentication password.
|
||||
* @param duplex Flag indicating full-duplex operation.
|
||||
* @param debug Flag indicating whether network debug is enabled.
|
||||
* @param dmr Flag indicating whether DMR is enabled.
|
||||
* @param p25 Flag indicating whether P25 is enabled.
|
||||
* @param nxdn Flag indicating whether NXDN is enabled.
|
||||
* @param slot1 Flag indicating whether DMR slot 1 is enabled for network traffic.
|
||||
* @param slot2 Flag indicating whether DMR slot 2 is enabled for network traffic.
|
||||
* @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network.
|
||||
* @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network.
|
||||
* @param updateLookup Flag indicating that the system will accept radio ID and talkgroup ID lookups from the network.
|
||||
*/
|
||||
DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password,
|
||||
bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup);
|
||||
|
||||
/**
|
||||
* @brief Writes P25 LDU1 frame data to the network.
|
||||
* @param[in] control Instance of p25::lc::LC containing link control data.
|
||||
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
|
||||
* @param[in] data Buffer containing P25 LDU1 data to send.
|
||||
* @param[in] frameType DVM P25 frame type.
|
||||
* @returns bool True, if message was sent, otherwise false.
|
||||
*/
|
||||
bool writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data,
|
||||
P25DEF::FrameType::E frameType) override;
|
||||
/**
|
||||
* @brief Writes P25 LDU2 frame data to the network.
|
||||
* @param[in] control Instance of p25::lc::LC containing link control data.
|
||||
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
|
||||
* @param[in] data Buffer containing P25 LDU2 data to send.
|
||||
* @returns bool True, if message was sent, otherwise false.
|
||||
*/
|
||||
bool writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Writes configuration to the network.
|
||||
* @returns bool True, if configuration was sent, otherwise false.
|
||||
*/
|
||||
bool writeConfig() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Creates an P25 LDU1 frame message.
|
||||
*
|
||||
* The data packed into a P25 LDU1 frame message is near standard DFSI messaging, just instead of
|
||||
* 9 individual frames, they are packed into a single message one right after another.
|
||||
*
|
||||
* @param[out] length Length of network message buffer.
|
||||
* @param[in] control Instance of p25::lc::LC containing link control data.
|
||||
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
|
||||
* @param[in] data Buffer containing P25 LDU1 data to send.
|
||||
* @param[in] frameType DVM P25 frame type.
|
||||
* @returns UInt8Array Buffer containing the built network message.
|
||||
*/
|
||||
UInt8Array createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||
const uint8_t* data, P25DEF::FrameType::E frameType);
|
||||
/**
|
||||
* @brief Creates an P25 LDU2 frame message.
|
||||
*
|
||||
* The data packed into a P25 LDU2 frame message is near standard DFSI messaging, just instead of
|
||||
* 9 individual frames, they are packed into a single message one right after another.
|
||||
*
|
||||
* @param[out] length Length of network message buffer.
|
||||
* @param[in] control Instance of p25::lc::LC containing link control data.
|
||||
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
|
||||
* @param[in] data Buffer containing P25 LDU2 data to send.
|
||||
* @returns UInt8Array Buffer containing the built network message.
|
||||
*/
|
||||
UInt8Array createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||
const uint8_t* data);
|
||||
};
|
||||
} // namespace network
|
||||
|
||||
#endif // __DFSI_PEER_NETWORK_H__
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,264 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - DFSI V.24/UDP Software
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @defgroup dfsi_network DFSI Networking/Serial Communications
|
||||
* @brief Implementation for the DFSI networking and serial communications.
|
||||
* @ingroup dfsi
|
||||
*
|
||||
* @file SerialService.h
|
||||
* @ingroup dfsi_network
|
||||
* @file SerialService.cpp
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
#if !defined(__SERIAL_SERVICE_H__)
|
||||
#define __SERIAL_SERVICE_H__
|
||||
|
||||
#include "Defines.h"
|
||||
#include "common/edac/RS634717.h"
|
||||
#include "common/network/RawFrameQueue.h"
|
||||
#include "common/p25/data/LowSpeedData.h"
|
||||
#include "common/p25/P25Defines.h"
|
||||
#include "common/p25/dfsi/DFSIDefines.h"
|
||||
#include "common/p25/dfsi/LC.h"
|
||||
#include "common/p25/dfsi/frames/Frames.h"
|
||||
#include "common/Log.h"
|
||||
#include "common/Utils.h"
|
||||
#include "common/yaml/Yaml.h"
|
||||
#include "common/RingBuffer.h"
|
||||
#include "network/DfsiPeerNetwork.h"
|
||||
#include "network/CallData.h"
|
||||
#include "host/modem/Modem.h"
|
||||
#include "host/modem/port/IModemPort.h"
|
||||
#include "host/modem/port/UARTPort.h"
|
||||
#include "host/modem/port/ModemNullPort.h"
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <random>
|
||||
#include <pthread.h>
|
||||
|
||||
using namespace modem;
|
||||
using namespace p25;
|
||||
using namespace dfsi;
|
||||
|
||||
namespace network
|
||||
{
|
||||
/**
|
||||
* @brief DFSI serial tx flags used to determine proper jitter handling of data in ringbuffer.
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
enum SERIAL_TX_TYPE {
|
||||
NONIMBE,
|
||||
IMBE
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Implements the serial V.24 communications service.
|
||||
* @ingroup dfsi_network
|
||||
*/
|
||||
class HOST_SW_API SerialService {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes an instance of the SerialService class.
|
||||
* @param portType Serial port type.
|
||||
* @param portName Serial port device name.
|
||||
* @param baudrate Baud rate.
|
||||
* @param rtrt Flag indicating whether or not RT/RT is enabled.
|
||||
* @param diu Flag indicating whether or not V.24 communications are to a DIU.
|
||||
* @param jitter
|
||||
* @param network Instance of the DfsiPeerNetwork class.
|
||||
* @param p25TxQueueSize Ringbuffer size for the P25 transmit queue.
|
||||
* @param p25RxQueueSize Ringbuffer size for the P24 receive queue.
|
||||
* @param callTimeout
|
||||
* @param debug Flag indicating whether verbose debug logging is enabled.
|
||||
* @param trace Flag indicating whether verbose trace logging is enabled.
|
||||
*/
|
||||
SerialService(std::string& portType, const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, uint16_t callTimeout, bool debug, bool trace);
|
||||
/**
|
||||
* @brief Finalizes an instance of the SerialService class.
|
||||
*/
|
||||
~SerialService();
|
||||
|
||||
/**
|
||||
* @brief Updates the serial interface by the passed number of milliseconds.
|
||||
* @param ms Number of milliseconds.
|
||||
*/
|
||||
void clock(uint32_t ms);
|
||||
|
||||
/**
|
||||
* @brief Opens connection to the serial interface.
|
||||
* @returns bool True, if serial interface has started, otherwise false.
|
||||
*/
|
||||
bool open();
|
||||
|
||||
/**
|
||||
* @brief Closes connection to the serial interface.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief Process data frames from the network.
|
||||
* @param p25Buffer Buffer containing the network frame.
|
||||
* @param length Length of the buffer.
|
||||
*/
|
||||
void processP25FromNet(UInt8Array p25Buffer, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Process data frames from the V.24 serial interface.
|
||||
*
|
||||
* This function pieces together LDU1/LDU2 messages from individual DFSI frames received over the serial port. It's
|
||||
* called multiple times before an LDU is sent, and each time adds more data pieces to the LDUs.
|
||||
*/
|
||||
void processP25ToNet();
|
||||
|
||||
/**
|
||||
* @brief Send the CMD_GET_VERSION to the connected V24 board to get firmware/UDID info
|
||||
*/
|
||||
void getBoardInfo();
|
||||
|
||||
private:
|
||||
std::string m_portName;
|
||||
uint32_t m_baudrate;
|
||||
bool m_rtrt;
|
||||
bool m_diu;
|
||||
|
||||
port::IModemPort* m_port;
|
||||
uint16_t m_jitter;
|
||||
|
||||
bool m_debug;
|
||||
bool m_trace;
|
||||
|
||||
DfsiPeerNetwork* m_network;
|
||||
|
||||
uint8_t* m_lastIMBE;
|
||||
|
||||
// Tables to keep track of calls
|
||||
std::unordered_map<uint32_t, uint64_t> m_lastHeard;
|
||||
std::unordered_map<uint32_t, uint32_t> m_sequences;
|
||||
|
||||
uint8_t* m_msgBuffer;
|
||||
RESP_STATE m_msgState;
|
||||
uint16_t m_msgLength;
|
||||
uint16_t m_msgOffset;
|
||||
DVM_COMMANDS m_msgType;
|
||||
bool m_msgDoubleLength;
|
||||
|
||||
uint32_t m_netFrames;
|
||||
uint32_t m_netLost;
|
||||
|
||||
RingBuffer<uint8_t> m_rxP25Queue;
|
||||
RingBuffer<uint8_t> m_txP25Queue;
|
||||
|
||||
// Storage for V24 TX jitter buffer metering
|
||||
uint64_t m_lastP25Tx;
|
||||
|
||||
edac::RS634717 m_rs;
|
||||
|
||||
// Counter for assembling a full LDU from individual frames in the RX queue
|
||||
uint8_t m_rxP25LDUCounter;
|
||||
|
||||
// "Mutex" flags to indicate if calls to/from the FNE are already in progress
|
||||
bool m_netCallInProgress;
|
||||
bool m_lclCallInProgress;
|
||||
|
||||
// Time in ms to wait before considering a call in progress as "over" in case we miss the TDUs
|
||||
uint16_t m_callTimeout;
|
||||
|
||||
// Storage for handling local/net call timeouts (for callInProgress mutexes)
|
||||
uint64_t m_lastNetFrame;
|
||||
uint64_t m_lastLclFrame;
|
||||
|
||||
// Control and LSD objects for current RX P25 data being built to send to the net
|
||||
lc::LC* m_rxVoiceControl;
|
||||
data::LowSpeedData* m_rxVoiceLsd;
|
||||
|
||||
// Call Data object for current RX P25 call (VHDR, LDU, etc)
|
||||
VoiceCallData* m_rxVoiceCallData;
|
||||
|
||||
// The last LDU1 LC sent to the net, used to keep track of current call src/dst IDs, etc
|
||||
p25::lc::LC* m_rxLastLDU1;
|
||||
|
||||
// Functions called by clock() to read/write from/to the serial port
|
||||
/**
|
||||
* @brief Read a data message from the serial interface.
|
||||
* @return RESP_TYPE_DVM
|
||||
*/
|
||||
RESP_TYPE_DVM readSerial();
|
||||
/**
|
||||
* @brief Helper to write data from the P25 Tx queue to the serial interface.
|
||||
*
|
||||
* Very similar to the readP25Frame function.
|
||||
*
|
||||
* Note: the length encoded at the start does not include the length, tag, or timestamp bytes.
|
||||
*
|
||||
* @return int Actual number of bytes written to the serial interface.
|
||||
*/
|
||||
int writeSerial();
|
||||
|
||||
/**
|
||||
* @brief Gets a frame of P25 data from the RX queue.
|
||||
* @param data Buffer containing P25 data.
|
||||
* @return uint32_t Size of the P25 data buffer, including leading data tag.
|
||||
*/
|
||||
uint32_t readP25Frame(uint8_t* data);
|
||||
/**
|
||||
* @brief Process a P25 LDU and add to the TX queue, timed appropriately.
|
||||
* @param duid DUID.
|
||||
* @param lc Instance of the dfsi::LC class.
|
||||
* @param ldu Buffer containing LDU data.
|
||||
*/
|
||||
void writeP25Frame(P25DEF::DUID::E duid, dfsi::LC& lc, uint8_t* ldu);
|
||||
|
||||
/**
|
||||
* @brief Send a start of stream sequence (HDU, etc) to the connected serial V24 device.
|
||||
* @param lc Instance of the p25::LC class.
|
||||
*/
|
||||
void startOfStream(const LC& lc);
|
||||
/**
|
||||
* @brief Send an end of stream sequence (TDU, etc) to the connected serial V24 device.
|
||||
*/
|
||||
void endOfStream();
|
||||
|
||||
/**
|
||||
* @brief Helper to add a V.24 dataframe to the P25 Tx queue with the proper timestamp and formatting.
|
||||
* @param data Buffer containing V.24 data frame to send.
|
||||
* @param len Length of buffer.
|
||||
* @param msgType Type of message to send (used for proper jitter clocking).
|
||||
*/
|
||||
void addTxToQueue(uint8_t* data, uint16_t length, SERIAL_TX_TYPE msgType);
|
||||
|
||||
/**
|
||||
* @brief Helper to insert IMBE silence frames for missing audio.
|
||||
* @param data Buffer containing frame data.
|
||||
* @param[out] lost Count of lost IMBE frames.
|
||||
*/
|
||||
void insertMissingAudio(uint8_t* data, uint32_t& lost);
|
||||
|
||||
/**
|
||||
* @brief Resets the current rx call data (stream ID, TGIDs, etc)
|
||||
*/
|
||||
void rxResetCallData();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param buffer
|
||||
* @param length
|
||||
*/
|
||||
void printDebug(const uint8_t* buffer, uint16_t length);
|
||||
};
|
||||
} // namespace network
|
||||
|
||||
#endif // __SERIAL_SERVICE_H__
|
||||
Loading…
Reference in new issue