Basic implementation of dvmdfsi in dvmhost C++ ecosystem (#59)
* initial bootstrap of CPP dvmdfsi * more work on serial service, the basics are there now * updated TODOs * more work on dfsi, getting there, just a few more things to implement * rough code finished, totally untested, hope it works * fixes for malloc errors, still not totally working * almost working, P25 voice from FNE is garbled but we're getting there * verified v24 decode/encode is working, still cleaning up serial TX * dvmdfsi ready for beta testing! A few gremlins to find but it works. * added configurable source flag option * fixed diu source flag config entry name * small update to Mot VHDR1 * fixed serial initialization, flags work now, config & code cleanup * log cleanups, added basic call collision logic, fixed kid being truncated to uint8_t * fixed LDU2 MI not getting copied properly * bring add-dvmdfsi up-to-date with master; * Add syslog and some project file cleanup (#58) * initial bootstrap of CPP dvmdfsi * more work on serial service, the basics are there now * updated TODOs * more work on dfsi, getting there, just a few more things to implement * rough code finished, totally untested, hope it works * fixes for malloc errors, still not totally working * almost working, P25 voice from FNE is garbled but we're getting there * verified v24 decode/encode is working, still cleaning up serial TX * dvmdfsi ready for beta testing! A few gremlins to find but it works. * added configurable source flag option * fixed diu source flag config entry name * small update to Mot VHDR1 * fixed serial initialization, flags work now, config & code cleanup * log cleanups, added basic call collision logic, fixed kid being truncated to uint8_t * fixed LDU2 MI not getting copied properly * add support for syslog logging in dvmdfsi; * add useSyslog parameter to log section of configuration file; * project cleanup: split MotRtpFrames.cpp/.h into separate files properly; ensure decode() functions pass the pointer as const to prevent accidental modification of input buffer; move common enums to a separate RtpDefines.h header; reuse MotRtpFrames.h (renamed RtpFrames.h) as a quick way of including all the RTP frames; --------- Co-authored-by: W3AXL <29879554+W3AXL@users.noreply.github.com> --------- Co-authored-by: W3AXL <29879554+W3AXL@users.noreply.github.com> Co-authored-by: Bryan Biedenkapp <gatekeep@gmail.com>pull/61/head
parent
243696855c
commit
0bbc69d237
@ -0,0 +1,99 @@
|
|||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# Serial configuration for serial DFSI
|
||||||
|
port: "/dev/ttyACM0"
|
||||||
|
baudrate: 115200
|
||||||
|
# RT/RT flag enabled (0x02) or disabled (0x04)
|
||||||
|
rtrt: false
|
||||||
|
# Use the DIU source flag (0x00) instead of the quantar source flag (0x02)
|
||||||
|
diu: false
|
||||||
|
# Jitter buffer length in ms
|
||||||
|
jitter: 200
|
||||||
|
# Debug logging
|
||||||
|
debug: false
|
||||||
|
# Trace logging (prints lots of data)
|
||||||
|
trace: false
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to open the activity log file, file handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True, if log file is opened, otherwise false.
|
||||||
|
static bool ActivityLogOpen()
|
||||||
|
{
|
||||||
|
if (CurrentLogFileLevel() == 0U)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
::time(&now);
|
||||||
|
|
||||||
|
struct tm* tm = ::gmtime(&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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the activity log.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">Full-path to the activity log file.</param>
|
||||||
|
/// <param name="fileRoot">Prefix of the activity log file name.</param>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes the activity log.
|
||||||
|
/// </summary>
|
||||||
|
void ActivityLogFinalise()
|
||||||
|
{
|
||||||
|
#if defined(CATCH2_TEST_COMPILATION)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
if (m_actFpLog != nullptr)
|
||||||
|
::fclose(m_actFpLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a new entry to the activity log.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This is a variable argument function.</remarks>
|
||||||
|
/// <param name="msg">Formatted string to write to activity log.</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__ACTIVITY_LOG_H__)
|
||||||
|
#define __ACTIVITY_LOG_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Global Functions
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>Initializes the activity log.</summary>
|
||||||
|
extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot);
|
||||||
|
/// <summary>Finalizes the activity log.</summary>
|
||||||
|
extern HOST_SW_API void ActivityLogFinalise();
|
||||||
|
/// <summary>Writes a new entry to the activity log.</summary>
|
||||||
|
extern HOST_SW_API void ActivityLog(const char* msg, ...);
|
||||||
|
|
||||||
|
#endif // __ACTIVITY_LOG_H__
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
# 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.
|
||||||
|
#*
|
||||||
|
#* @package DVM / DFSI peer application
|
||||||
|
#* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
#* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
#*
|
||||||
|
#* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
#*
|
||||||
|
#*/
|
||||||
|
file(GLOB dvmdfsi_SRC
|
||||||
|
# Modem libs for serial communication
|
||||||
|
"src/host/modem/*.h"
|
||||||
|
"src/host/modem/*.cpp"
|
||||||
|
"src/host/modem/port/*.h"
|
||||||
|
"src/host/modem/port/*.cpp"
|
||||||
|
# Core network libs
|
||||||
|
"src/host/network/Network.h"
|
||||||
|
"src/host/network/Network.cpp"
|
||||||
|
# DFSI network libs
|
||||||
|
"src/dfsi/network/*.h"
|
||||||
|
"src/dfsi/network/*.cpp"
|
||||||
|
# DFSI rtp libs
|
||||||
|
"src/dfsi/rtp/*.h"
|
||||||
|
"src/dfsi/rtp/*.cpp"
|
||||||
|
# Core DFSI
|
||||||
|
"src/dfsi/*.h"
|
||||||
|
"src/dfsi/*.cpp"
|
||||||
|
)
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__DEFINES_H__)
|
||||||
|
#define __DEFINES_H__
|
||||||
|
|
||||||
|
#include "common/Defines.h"
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#undef __PROG_NAME__
|
||||||
|
#define __PROG_NAME__ "Digital Voice Modem (DVM) DFSI 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"
|
||||||
|
|
||||||
|
#endif // __DEFINES_H__
|
||||||
@ -0,0 +1,400 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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 <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define IDLE_WARMUP_MS 5U
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HostTest class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="confFile">Full-path to the configuration file.</param>
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the HostTest class.
|
||||||
|
/// </summary>
|
||||||
|
Dfsi::~Dfsi() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the main FNE processing loop.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Zero if successful, otherwise error occurred.</returns>
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
// initialize peer networking
|
||||||
|
ret = createPeerNetwork();
|
||||||
|
if (!ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
::LogInfoEx(LOG_HOST, "DFSI peer network is up and running");
|
||||||
|
|
||||||
|
// Read DFSI config
|
||||||
|
yaml::Node dfsi_conf = m_conf["dfsi"];
|
||||||
|
uint32_t p25BufferSize = dfsi_conf["p25BufferSize"].as<uint32_t>();
|
||||||
|
|
||||||
|
// Read serial config
|
||||||
|
yaml::Node serial_conf = dfsi_conf["serial"];
|
||||||
|
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: %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(port, baudrate, rtrt, diu, jitter, m_network, p25BufferSize, p25BufferSize, serial_debug, serial_trace);
|
||||||
|
|
||||||
|
// Open serial
|
||||||
|
ret = m_serial->open();
|
||||||
|
if (!ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
StopWatch stopWatch;
|
||||||
|
stopWatch.start();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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) {
|
||||||
|
uint8_t duid = p25Buffer[22U];
|
||||||
|
uint8_t MFId = p25Buffer[15U];
|
||||||
|
|
||||||
|
uint8_t lco = p25Buffer[4U];
|
||||||
|
|
||||||
|
uint32_t srcId = __GET_UINT16(p25Buffer, 5U);
|
||||||
|
uint32_t dstId = __GET_UINT16(p25Buffer, 8U);
|
||||||
|
|
||||||
|
if (!g_hideMessages)
|
||||||
|
LogMessage(LOG_NET, "P25, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u", duid, lco, MFId, srcId, dstId, length);
|
||||||
|
|
||||||
|
// Send the data to the serial handler
|
||||||
|
m_serial->processP25FromNet(std::move(p25Buffer), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We keep DMR & NXDN in so nothing breaks, even though DFSI doesn't do DMR or NXDNS
|
||||||
|
UInt8Array dmrBuffer = m_network->readDMR(netReadRet, length);
|
||||||
|
if (netReadRet) {
|
||||||
|
uint8_t seqNo = dmrBuffer[4U];
|
||||||
|
|
||||||
|
uint32_t srcId = __GET_UINT16(dmrBuffer, 5U);
|
||||||
|
uint32_t dstId = __GET_UINT16(dmrBuffer, 8U);
|
||||||
|
|
||||||
|
uint8_t flco = (dmrBuffer[15U] & 0x40U) == 0x40U ? dmr::FLCO_PRIVATE : dmr::FLCO_GROUP;
|
||||||
|
|
||||||
|
uint32_t slotNo = (dmrBuffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
|
||||||
|
|
||||||
|
if (!g_hideMessages)
|
||||||
|
LogMessage(LOG_NET, "DMR, slotNo = %u, seqNo = %u, flco = $%02X, srcId = %u, dstId = %u, len = %u", slotNo, seqNo, flco, srcId, dstId, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt8Array nxdnBuffer = m_network->readNXDN(netReadRet, length);
|
||||||
|
if (netReadRet) {
|
||||||
|
uint8_t messageType = nxdnBuffer[4U];
|
||||||
|
|
||||||
|
uint32_t srcId = __GET_UINT16(nxdnBuffer, 5U);
|
||||||
|
uint32_t dstId = __GET_UINT16(nxdnBuffer, 8U);
|
||||||
|
|
||||||
|
if (!g_hideMessages)
|
||||||
|
LogMessage(LOG_NET, "NXDN, messageType = $%02X, srcId = %u, dstId = %u, len = %u", messageType, srcId, dstId, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// -- Network TX Clocking --
|
||||||
|
// ------------------------------------------------------
|
||||||
|
|
||||||
|
// Processes data in the serial rx P25 buffer and sends it to the network buffer for sending
|
||||||
|
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
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads basic configuration parameters from the YAML configuration file.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool Dfsi::readParams()
|
||||||
|
{
|
||||||
|
// No basic config params right now
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes peer network connectivity.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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
|
||||||
|
// This class implements the core service logic.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API Dfsi {
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the HostTest class.</summary>
|
||||||
|
Dfsi(const std::string& confFile);
|
||||||
|
/// <summary>Finalizes a instance of the HostTest class.</summary>
|
||||||
|
~Dfsi();
|
||||||
|
|
||||||
|
/// <summary>Executes the main host processing loop.</summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>Reads basic configuration parameters from the INI.</summary>
|
||||||
|
bool readParams();
|
||||||
|
/// <summary>Initializes peer network connectivity.</summary>
|
||||||
|
bool createPeerNetwork();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __DFSI_H__
|
||||||
@ -0,0 +1,247 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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)
|
||||||
|
/// <summary>
|
||||||
|
/// Internal signal handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signum"></param>
|
||||||
|
static void sigHandler(int signum)
|
||||||
|
{
|
||||||
|
g_signal = signum;
|
||||||
|
g_killed = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to print a fatal error message and exit.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This is a variable argument function.</remarks>
|
||||||
|
/// <param name="msg">Message.</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to pring usage the command line arguments. (And optionally an error.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">Error message.</param>
|
||||||
|
/// <param name="arg">Error message arguments.</param>
|
||||||
|
void usage(const char* message, const char* arg)
|
||||||
|
{
|
||||||
|
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
|
||||||
|
::fprintf(stdout, "Copyright (c) 2024 DVMProject (https://github.com/dvmproject) Authors.\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>]"
|
||||||
|
"[-a <address>] [-p <port>] [-P <peer id>]"
|
||||||
|
"\n\n"
|
||||||
|
" -v show version information\n"
|
||||||
|
" -h show this screen\n"
|
||||||
|
" -f foreground mode\n"
|
||||||
|
"\n"
|
||||||
|
" -c <file> specifies the configuration file to use\n"
|
||||||
|
"\n"
|
||||||
|
" -- stop handling options\n",
|
||||||
|
g_progExe.c_str());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to validate the command line arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="argc">Argument count.</param>
|
||||||
|
/// <param name="argv">Array of argument strings.</param>
|
||||||
|
/// <returns>Count of remaining unprocessed arguments.</returns>
|
||||||
|
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("-s")) {
|
||||||
|
g_hideMessages = 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) 2017-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, "Exited on receipt of SIGINT");
|
||||||
|
|
||||||
|
if (g_signal == 15)
|
||||||
|
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM");
|
||||||
|
|
||||||
|
if (g_signal == 1)
|
||||||
|
::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP");
|
||||||
|
} while (g_signal == 1);
|
||||||
|
|
||||||
|
::LogFinalise();
|
||||||
|
::ActivityLogFinalise();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__DFSI_MAIN_H__)
|
||||||
|
#define __DFSI_MAIN_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Externs
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
extern int g_signal;
|
||||||
|
extern std::string g_progExe;
|
||||||
|
extern std::string g_iniFile;
|
||||||
|
extern std::string g_lockFile;
|
||||||
|
|
||||||
|
extern bool g_foreground;
|
||||||
|
extern bool g_killed;
|
||||||
|
extern bool g_hideMessages;
|
||||||
|
|
||||||
|
extern std::string g_masterAddress;
|
||||||
|
extern uint16_t g_masterPort;
|
||||||
|
extern uint32_t g_peerId;
|
||||||
|
|
||||||
|
extern uint8_t* g_gitHashBytes;
|
||||||
|
|
||||||
|
extern HOST_SW_API void fatal(const char* msg, ...);
|
||||||
|
|
||||||
|
#endif // __DFSI_MAIN_H__
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
* Borrowed from work by Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CallData.h"
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
using namespace modem;
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
VoiceCallData::VoiceCallData() :
|
||||||
|
srcId(0U),
|
||||||
|
dstId(0U),
|
||||||
|
lco(0U),
|
||||||
|
mfId(P25_MFG_STANDARD),
|
||||||
|
serviceOptions(0U),
|
||||||
|
lsd1(0U),
|
||||||
|
lsd2(0U),
|
||||||
|
mi(),
|
||||||
|
algoId(P25_ALGO_UNENCRYPT),
|
||||||
|
kId(0U),
|
||||||
|
VHDR1(),
|
||||||
|
VHDR2(),
|
||||||
|
netLDU1(),
|
||||||
|
netLDU2(),
|
||||||
|
seqNo(0U),
|
||||||
|
n(0U),
|
||||||
|
streamId(0U)
|
||||||
|
{
|
||||||
|
mi = new uint8_t[P25_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
VoiceCallData::~VoiceCallData() {
|
||||||
|
delete[] mi;
|
||||||
|
delete[] VHDR1;
|
||||||
|
delete[] VHDR2;
|
||||||
|
delete[] netLDU1;
|
||||||
|
delete[] netLDU2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoiceCallData::resetCallData() {
|
||||||
|
srcId = 0U;
|
||||||
|
dstId = 0U;
|
||||||
|
lco = 0U;
|
||||||
|
mfId = P25_MFG_STANDARD;
|
||||||
|
serviceOptions = 0U;
|
||||||
|
lsd1 = 0U;
|
||||||
|
lsd2 = 0U;
|
||||||
|
|
||||||
|
::memset(mi, 0x00U, P25_MI_LENGTH_BYTES);
|
||||||
|
|
||||||
|
algoId = P25_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoiceCallData::newStreamId() {
|
||||||
|
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
|
||||||
|
streamId = dist(random);
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
* Borrowed from work by Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__DFSI_CALL_DATA_H__)
|
||||||
|
#define __DFSI_CALL_DATA_H__
|
||||||
|
|
||||||
|
// DVM Includes
|
||||||
|
#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/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/yaml/Yaml.h"
|
||||||
|
#include "common/RingBuffer.h"
|
||||||
|
#include "network/DfsiPeerNetwork.h"
|
||||||
|
#include "rtp/RtpFrames.h"
|
||||||
|
#include "host/modem/Modem.h"
|
||||||
|
#include "host/modem/port/IModemPort.h"
|
||||||
|
#include "host/modem/port/UARTPort.h"
|
||||||
|
|
||||||
|
// CPP includes
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
class HOST_SW_API VoiceCallData {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VoiceCallData();
|
||||||
|
~VoiceCallData();
|
||||||
|
|
||||||
|
void resetCallData();
|
||||||
|
|
||||||
|
void newStreamId();
|
||||||
|
|
||||||
|
// Call Data
|
||||||
|
uint32_t srcId;
|
||||||
|
uint32_t dstId;
|
||||||
|
|
||||||
|
uint8_t lco;
|
||||||
|
uint8_t mfId;
|
||||||
|
uint8_t serviceOptions;
|
||||||
|
|
||||||
|
uint8_t lsd1;
|
||||||
|
uint8_t lsd2;
|
||||||
|
|
||||||
|
uint8_t* mi;
|
||||||
|
uint8_t algoId;
|
||||||
|
uint32_t kId;
|
||||||
|
|
||||||
|
uint8_t* VHDR1;
|
||||||
|
uint8_t* VHDR2;
|
||||||
|
|
||||||
|
uint8_t* netLDU1;
|
||||||
|
uint8_t* netLDU2;
|
||||||
|
|
||||||
|
uint32_t seqNo;
|
||||||
|
uint8_t n;
|
||||||
|
|
||||||
|
uint32_t streamId;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Used for stream ID generation
|
||||||
|
std::mt19937 random;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __DFSI_CALL_DATA_H__
|
||||||
@ -0,0 +1,337 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the PeerNetwork class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Network Hostname/IP address to connect to.</param>
|
||||||
|
/// <param name="port">Network port number.</param>
|
||||||
|
/// <param name="local"></param>
|
||||||
|
/// <param name="peerId">Unique ID on the network.</param>
|
||||||
|
/// <param name="password">Network authentication password.</param>
|
||||||
|
/// <param name="duplex">Flag indicating full-duplex operation.</param>
|
||||||
|
/// <param name="debug">Flag indicating whether network debug is enabled.</param>
|
||||||
|
/// <param name="dmr">Flag indicating whether DMR is enabled.</param>
|
||||||
|
/// <param name="p25">Flag indicating whether P25 is enabled.</param>
|
||||||
|
/// <param name="nxdn">Flag indicating whether NXDN is enabled.</param>
|
||||||
|
/// <param name="slot1">Flag indicating whether DMR slot 1 is enabled for network traffic.</param>
|
||||||
|
/// <param name="slot2">Flag indicating whether DMR slot 2 is enabled for network traffic.</param>
|
||||||
|
/// <param name="allowActivityTransfer">Flag indicating that the system activity logs will be sent to the network.</param>
|
||||||
|
/// <param name="allowDiagnosticTransfer">Flag indicating that the system diagnostic logs will be sent to the network.</param>
|
||||||
|
/// <param name="updateLookup">Flag indicating that the system will accept radio ID and talkgroup ID lookups from the network.</param>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes P25 LDU1 frame data to the network.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control"></param>
|
||||||
|
/// <param name="lsd"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="frameType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool DfsiPeerNetwork::writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, uint8_t 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_PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes P25 LDU2 frame data to the network.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control"></param>
|
||||||
|
/// <param name="lsd"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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_PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Protected Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes configuration to the network.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
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
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an P25 LDU1 frame message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length"></param>
|
||||||
|
/// <param name="control"></param>
|
||||||
|
/// <param name="lsd"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="frameType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
UInt8Array DfsiPeerNetwork::createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||||
|
const uint8_t* data, uint8_t frameType)
|
||||||
|
{
|
||||||
|
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, p25::P25_DUID_LDU1, control, lsd, frameType);
|
||||||
|
|
||||||
|
// pack DFSI data
|
||||||
|
uint32_t count = MSG_HDR_SIZE;
|
||||||
|
uint8_t imbe[p25::P25_RAW_IMBE_LENGTH_BYTES];
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE1);
|
||||||
|
::memcpy(imbe, data + 10U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 24U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE1_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE2);
|
||||||
|
::memcpy(imbe, data + 26U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 46U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE2_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE3);
|
||||||
|
::memcpy(imbe, data + 55U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 60U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE3_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE4);
|
||||||
|
::memcpy(imbe, data + 80U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 77U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE4_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE5);
|
||||||
|
::memcpy(imbe, data + 105U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 94U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE5_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE6);
|
||||||
|
::memcpy(imbe, data + 130U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 111U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE6_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE7);
|
||||||
|
::memcpy(imbe, data + 155U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 128U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE7_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE8);
|
||||||
|
::memcpy(imbe, data + 180U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 145U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU1_VOICE8_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU1_VOICE9);
|
||||||
|
::memcpy(imbe, data + 204U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU1(buffer + 162U, imbe);
|
||||||
|
count += p25::dfsi::P25_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an P25 LDU2 frame message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length"></param>
|
||||||
|
/// <param name="control"></param>
|
||||||
|
/// <param name="lsd"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
UInt8Array DfsiPeerNetwork::createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||||
|
const uint8_t* data)
|
||||||
|
{
|
||||||
|
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, p25::P25_DUID_LDU2, control, lsd, p25::P25_FT_DATA_UNIT);
|
||||||
|
|
||||||
|
// pack DFSI data
|
||||||
|
uint32_t count = MSG_HDR_SIZE;
|
||||||
|
uint8_t imbe[p25::P25_RAW_IMBE_LENGTH_BYTES];
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE10);
|
||||||
|
::memcpy(imbe, data + 10U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 24U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE10_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE11);
|
||||||
|
::memcpy(imbe, data + 26U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 46U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE11_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE12);
|
||||||
|
::memcpy(imbe, data + 55U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 60U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE12_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE13);
|
||||||
|
::memcpy(imbe, data + 80U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 77U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE13_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE14);
|
||||||
|
::memcpy(imbe, data + 105U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 94U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE14_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE15);
|
||||||
|
::memcpy(imbe, data + 130U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 111U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE15_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE16);
|
||||||
|
::memcpy(imbe, data + 155U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 128U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE16_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE17);
|
||||||
|
::memcpy(imbe, data + 180U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 145U, imbe);
|
||||||
|
count += p25::dfsi::P25_DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES;
|
||||||
|
|
||||||
|
dfsiLC.setFrameType(p25::dfsi::P25_DFSI_LDU2_VOICE18);
|
||||||
|
::memcpy(imbe, data + 204U, p25::P25_RAW_IMBE_LENGTH_BYTES);
|
||||||
|
dfsiLC.encodeLDU2(buffer + 162U, imbe);
|
||||||
|
count += p25::dfsi::P25_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);
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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
|
||||||
|
// Implements the core peer networking logic.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API DfsiPeerNetwork : public Network {
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the PeerNetwork class.</summary>
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// <summary>Writes P25 LDU1 frame data to the network.</summary>
|
||||||
|
bool writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data,
|
||||||
|
uint8_t frameType) override;
|
||||||
|
/// <summary>Writes P25 LDU2 frame data to the network.</summary>
|
||||||
|
bool writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// <summary>Writes configuration to the network.</summary>
|
||||||
|
bool writeConfig() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// <summary>Creates an P25 LDU1 frame message.</summary>
|
||||||
|
UInt8Array createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
|
||||||
|
const uint8_t* data, uint8_t frameType);
|
||||||
|
/// <summary>Creates an P25 LDU2 frame message.</summary>
|
||||||
|
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
@ -0,0 +1,157 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__SERIAL_SERVICE_H__)
|
||||||
|
#define __SERIAL_SERVICE_H__
|
||||||
|
|
||||||
|
// DVM Includes
|
||||||
|
#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/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/yaml/Yaml.h"
|
||||||
|
#include "common/RingBuffer.h"
|
||||||
|
#include "network/DfsiPeerNetwork.h"
|
||||||
|
#include "network/CallData.h"
|
||||||
|
#include "rtp/RtpFrames.h"
|
||||||
|
#include "host/modem/Modem.h"
|
||||||
|
#include "host/modem/port/IModemPort.h"
|
||||||
|
#include "host/modem/port/UARTPort.h"
|
||||||
|
|
||||||
|
// System Includes
|
||||||
|
#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
|
||||||
|
{
|
||||||
|
|
||||||
|
// DFSI serial tx flags used to determine proper jitter handling of data in ringbuffer
|
||||||
|
enum SERIAL_TX_TYPE {
|
||||||
|
NONIMBE,
|
||||||
|
IMBE
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Serial V24 service
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SerialService {
|
||||||
|
public:
|
||||||
|
SerialService(const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, bool debug, bool trace);
|
||||||
|
|
||||||
|
~SerialService();
|
||||||
|
|
||||||
|
void clock(uint32_t ms);
|
||||||
|
|
||||||
|
bool open();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// Handle P25 data from network to V24
|
||||||
|
void processP25FromNet(UInt8Array p25Buffer, uint32_t length);
|
||||||
|
|
||||||
|
// Handle P25 data from V24 to network
|
||||||
|
void processP25ToNet();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Flags to indicate if calls to/from the FNE are already in progress
|
||||||
|
bool m_netCallInProgress;
|
||||||
|
bool m_lclCallInProgress;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Functions called by clock() to read/write from/to the serial port
|
||||||
|
RESP_TYPE_DVM readSerial();
|
||||||
|
int writeSerial();
|
||||||
|
|
||||||
|
uint32_t readP25Frame(uint8_t* data);
|
||||||
|
void writeP25Frame(uint8_t duid, dfsi::LC& lc, uint8_t* ldu);
|
||||||
|
|
||||||
|
// Helpers for TX stream data
|
||||||
|
void startOfStream(const LC& lc);
|
||||||
|
void endOfStream();
|
||||||
|
|
||||||
|
// Helper for timing TX data appropriately based on frame type
|
||||||
|
void addTxToQueue(uint8_t* data, uint16_t length, SERIAL_TX_TYPE msgType);
|
||||||
|
|
||||||
|
/// <summary>Helper to insert IMBE silence frames for missing audio.</summary>
|
||||||
|
void insertMissingAudio(uint8_t* data, uint32_t& lost);
|
||||||
|
|
||||||
|
void printDebug(const uint8_t* buffer, uint16_t length);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defines for Mot DFSI
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
|
||||||
|
#endif // __SERIAL_SERVICE_H__
|
||||||
@ -0,0 +1,211 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotFullRateVoice.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotFullRateVoice class.
|
||||||
|
/// </summary>
|
||||||
|
MotFullRateVoice::MotFullRateVoice()
|
||||||
|
{
|
||||||
|
frameType = P25_DFSI_LDU1_VOICE1;
|
||||||
|
additionalData = nullptr;
|
||||||
|
source = SOURCE_QUANTAR;
|
||||||
|
imbeData = new uint8_t[IMBE_BUF_LEN];
|
||||||
|
::memset(imbeData, 0x00U, IMBE_BUF_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotFullRateVoice class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
MotFullRateVoice::MotFullRateVoice(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the MotFullRateVoice class.
|
||||||
|
/// </summary>
|
||||||
|
MotFullRateVoice::~MotFullRateVoice()
|
||||||
|
{
|
||||||
|
delete[] imbeData;
|
||||||
|
delete[] additionalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
uint32_t MotFullRateVoice::size()
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
// Set length appropriately based on frame type
|
||||||
|
if (isVoice1or2or10or11()) {
|
||||||
|
length += SHORTENED_LENGTH;
|
||||||
|
} else {
|
||||||
|
length += LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are weird
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
length -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode a full rate voice frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="shortened"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotFullRateVoice::decode(const uint8_t* data, bool shortened)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
imbeData = new uint8_t[IMBE_BUF_LEN];
|
||||||
|
::memset(imbeData, 0x00U, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
frameType = data[0U];
|
||||||
|
|
||||||
|
if (isVoice2or11()) {
|
||||||
|
shortened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (shortened) {
|
||||||
|
::memcpy(imbeData, data + 1U, IMBE_BUF_LEN);
|
||||||
|
source = (SourceFlag)data[12U];
|
||||||
|
// Forgot to set this originally and left additionalData uninitialized, whoops!
|
||||||
|
additionalData = nullptr;
|
||||||
|
} else {
|
||||||
|
// Frames 0x6A and 0x73 are missing the 0x00 padding byte, so we start IMBE data 1 byte earlier
|
||||||
|
uint8_t imbeStart = 5U;
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
imbeStart = 4U;
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalData = new uint8_t[ADDITIONAL_LENGTH];
|
||||||
|
::memset(additionalData, 0x00U, ADDITIONAL_LENGTH);
|
||||||
|
::memcpy(additionalData, data + 1U, ADDITIONAL_LENGTH);
|
||||||
|
|
||||||
|
// Copy IMBE data based on our imbe start position
|
||||||
|
::memcpy(imbeData, data + imbeStart, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
source = (SourceFlag)data[IMBE_BUF_LEN + imbeStart];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode a full rate voice frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="shortened"></param>
|
||||||
|
void MotFullRateVoice::encode(uint8_t* data, bool shortened)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Check if we're a shortened frame
|
||||||
|
data[0U] = frameType;
|
||||||
|
if (isVoice2or11()) {
|
||||||
|
shortened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy based on shortened frame or not
|
||||||
|
if (shortened) {
|
||||||
|
::memcpy(data + 1U, imbeData, IMBE_BUF_LEN);
|
||||||
|
data[12U] = (uint8_t)source;
|
||||||
|
}
|
||||||
|
// If not shortened, our IMBE data start position depends on frame type
|
||||||
|
else {
|
||||||
|
// Starting index for the IMBE data
|
||||||
|
uint8_t imbeStart = 5U;
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
imbeStart = 4U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have additional data
|
||||||
|
if (additionalData != nullptr) {
|
||||||
|
::memcpy(data + 1U, additionalData, ADDITIONAL_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy rest of data
|
||||||
|
::memcpy(data + imbeStart, imbeData, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
// Source byte at the end
|
||||||
|
data[11U + imbeStart] = (uint8_t)source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Protected Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotFullRateVoice::isVoice1or2or10or11()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE1) || (frameType == P25_DFSI_LDU1_VOICE2) || (frameType == P25_DFSI_LDU2_VOICE10) || (frameType == P25_DFSI_LDU2_VOICE11) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotFullRateVoice::isVoice2or11()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE2) || (frameType == P25_DFSI_LDU2_VOICE11) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotFullRateVoice::isVoice9or18()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE9) || (frameType == P25_DFSI_LDU2_VOICE18) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_FULL_RATE_VOICE_H__)
|
||||||
|
#define __MOT_FULL_RATE_VOICE_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "rtp/RtpDefines.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Implements a P25 Motorola full rate voice packet.
|
||||||
|
//
|
||||||
|
// Byte 0 1 2 3
|
||||||
|
// Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | FT | Addtl Data | Addtl Data | Addtl Data |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Reserved | IMBE 1 | IMBE 2 | IMBE 3 |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | IMBE 4 | IMBE 5 | IMBE 6 | IMBE 7 |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | IMBE 8 | IMBE 9 | IMBE 10 | IMBE 11 |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Src Flag |
|
||||||
|
// +=+=+=+=+=+=+=+=+
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API MotFullRateVoice {
|
||||||
|
public:
|
||||||
|
// Frame information
|
||||||
|
static const uint8_t LENGTH = 17;
|
||||||
|
static const uint8_t SHORTENED_LENGTH = 13;
|
||||||
|
static const uint8_t ADDITIONAL_LENGTH = 4;
|
||||||
|
static const uint8_t IMBE_BUF_LEN = 11;
|
||||||
|
|
||||||
|
uint8_t frameType;
|
||||||
|
uint8_t* imbeData;
|
||||||
|
uint8_t* additionalData;
|
||||||
|
SourceFlag source;
|
||||||
|
|
||||||
|
/// <summary>Initializes a copy instance of the MotFullRateVoice class.</summary>
|
||||||
|
MotFullRateVoice();
|
||||||
|
/// <summary>Initializes a copy instance of the MotFullRateVoice class.</summary>
|
||||||
|
MotFullRateVoice(uint8_t* data);
|
||||||
|
/// <summary>Finalizes a instance of the MotFullRateVoice class.</summary>
|
||||||
|
~MotFullRateVoice();
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
uint32_t size();
|
||||||
|
/// <summary>Decode a full rate voice frame.</summary>
|
||||||
|
bool decode(const uint8_t* data, bool shortened = false);
|
||||||
|
/// <summary>Encode a full rate voice frame.</summary>
|
||||||
|
void encode(uint8_t* data, bool shortened = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// <summary></summary>
|
||||||
|
bool isVoice1or2or10or11();
|
||||||
|
/// <summary></summary>
|
||||||
|
bool isVoice2or11();
|
||||||
|
/// <summary></summary>
|
||||||
|
bool isVoice9or18();
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __MOT_FULL_RATE_VOICE_H__
|
||||||
@ -0,0 +1,432 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotRtpFrames.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Motorola full rate voice data
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MotFullRateVoice::MotFullRateVoice()
|
||||||
|
{
|
||||||
|
frameType = P25_DFSI_LDU1_VOICE1;
|
||||||
|
additionalData = nullptr;
|
||||||
|
source = SOURCE_QUANTAR;
|
||||||
|
imbeData = new uint8_t[IMBE_BUF_LEN];
|
||||||
|
::memset(imbeData, 0x00U, IMBE_BUF_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotFullRateVoice::MotFullRateVoice(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotFullRateVoice::~MotFullRateVoice()
|
||||||
|
{
|
||||||
|
delete[] imbeData;
|
||||||
|
delete[] additionalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MotFullRateVoice::size()
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
// Set length appropriately based on frame type
|
||||||
|
if (isVoice1or2or10or11()) {
|
||||||
|
length += SHORTENED_LENGTH;
|
||||||
|
} else {
|
||||||
|
length += LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are weird
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
length -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Decode a block of bytes into a full rate voice IMBE block</summary>
|
||||||
|
bool MotFullRateVoice::decode(uint8_t* data, bool shortened)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
imbeData = new uint8_t[IMBE_BUF_LEN];
|
||||||
|
::memset(imbeData, 0x00U, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
frameType = data[0U];
|
||||||
|
|
||||||
|
if (isVoice2or11()) {
|
||||||
|
shortened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (shortened) {
|
||||||
|
::memcpy(imbeData, data + 1U, IMBE_BUF_LEN);
|
||||||
|
source = (SourceFlag)data[12U];
|
||||||
|
// Forgot to set this originally and left additionalData uninitialized, whoops!
|
||||||
|
additionalData = nullptr;
|
||||||
|
} else {
|
||||||
|
// Frames 0x6A and 0x73 are missing the 0x00 padding byte, so we start IMBE data 1 byte earlier
|
||||||
|
uint8_t imbeStart = 5U;
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
imbeStart = 4U;
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalData = new uint8_t[ADDITIONAL_LENGTH];
|
||||||
|
::memset(additionalData, 0x00U, ADDITIONAL_LENGTH);
|
||||||
|
::memcpy(additionalData, data + 1U, ADDITIONAL_LENGTH);
|
||||||
|
|
||||||
|
// Copy IMBE data based on our imbe start position
|
||||||
|
::memcpy(imbeData, data + imbeStart, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
source = (SourceFlag)data[IMBE_BUF_LEN + imbeStart];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotFullRateVoice::encode(uint8_t* data, bool shortened)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Check if we're a shortened frame
|
||||||
|
data[0U] = frameType;
|
||||||
|
if (isVoice2or11()) {
|
||||||
|
shortened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy based on shortened frame or not
|
||||||
|
if (shortened) {
|
||||||
|
::memcpy(data + 1U, imbeData, IMBE_BUF_LEN);
|
||||||
|
data[12U] = (uint8_t)source;
|
||||||
|
}
|
||||||
|
// If not shortened, our IMBE data start position depends on frame type
|
||||||
|
else {
|
||||||
|
// Starting index for the IMBE data
|
||||||
|
uint8_t imbeStart = 5U;
|
||||||
|
if (isVoice9or18()) {
|
||||||
|
imbeStart = 4U;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have additional data
|
||||||
|
if (additionalData != nullptr) {
|
||||||
|
::memcpy(data + 1U, additionalData, ADDITIONAL_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy rest of data
|
||||||
|
::memcpy(data + imbeStart, imbeData, IMBE_BUF_LEN);
|
||||||
|
|
||||||
|
// Source byte at the end
|
||||||
|
data[11U + imbeStart] = (uint8_t)source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotFullRateVoice::isVoice1or2or10or11()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE1) || (frameType == P25_DFSI_LDU1_VOICE2) || (frameType == P25_DFSI_LDU2_VOICE10) || (frameType == P25_DFSI_LDU2_VOICE11) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotFullRateVoice::isVoice2or11()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE2) || (frameType == P25_DFSI_LDU2_VOICE11) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotFullRateVoice::isVoice9or18()
|
||||||
|
{
|
||||||
|
if ( (frameType == P25_DFSI_LDU1_VOICE9) || (frameType == P25_DFSI_LDU2_VOICE18) ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Motorola start of stream frame (10 bytes long)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MotStartOfStream::MotStartOfStream()
|
||||||
|
{
|
||||||
|
rt = DISABLED;
|
||||||
|
startStop = START;
|
||||||
|
streamType = VOICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotStartOfStream::MotStartOfStream(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotStartOfStream::decode(uint8_t* data)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Get parameters
|
||||||
|
rt = (RTFlag)data[2U];
|
||||||
|
startStop = (StartStopFlag)data[3U];
|
||||||
|
streamType = (StreamTypeFlag)data[4U];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotStartOfStream::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Copy data
|
||||||
|
data[0U] = P25_DFSI_MOT_START_STOP;
|
||||||
|
data[1U] = FIXED_MARKER;
|
||||||
|
data[2U] = rt;
|
||||||
|
data[3U] = startStop;
|
||||||
|
data[4U] = streamType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Motorola start voice frame
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MotStartVoiceFrame::MotStartVoiceFrame()
|
||||||
|
{
|
||||||
|
icw = ICW_DIU;
|
||||||
|
rssi = 0;
|
||||||
|
rssiValidity = INVALID;
|
||||||
|
nRssi = 0;
|
||||||
|
adjMM = 0;
|
||||||
|
|
||||||
|
startOfStream = nullptr;
|
||||||
|
fullRateVoice = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotStartVoiceFrame::MotStartVoiceFrame(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotStartVoiceFrame::~MotStartVoiceFrame()
|
||||||
|
{
|
||||||
|
delete startOfStream;
|
||||||
|
delete fullRateVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotStartVoiceFrame::decode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Create a new startOfStream
|
||||||
|
startOfStream = new MotStartOfStream();
|
||||||
|
|
||||||
|
// Create a buffer to decode the start record skipping the 10th byte (adjMM)
|
||||||
|
uint8_t startBuffer[startOfStream->LENGTH];
|
||||||
|
::memset(startBuffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
::memcpy(startBuffer, data, 9U);
|
||||||
|
|
||||||
|
// Decode start of stream
|
||||||
|
startOfStream->decode(startBuffer);
|
||||||
|
|
||||||
|
// Decode the full rate voice frames
|
||||||
|
fullRateVoice = new MotFullRateVoice();
|
||||||
|
uint8_t voiceBuffer[fullRateVoice->SHORTENED_LENGTH];
|
||||||
|
::memset(voiceBuffer, 0x00U, fullRateVoice->SHORTENED_LENGTH);
|
||||||
|
voiceBuffer[0U] = data[0U];
|
||||||
|
::memcpy(voiceBuffer + 1U, data + 10U, fullRateVoice->SHORTENED_LENGTH - 1);
|
||||||
|
fullRateVoice->decode(voiceBuffer, true);
|
||||||
|
|
||||||
|
// Get rest of data
|
||||||
|
icw = (ICWFlag)data[5U];
|
||||||
|
rssi = data[6U];
|
||||||
|
rssiValidity = (RssiValidityFlag)data[7U];
|
||||||
|
nRssi = data[8U];
|
||||||
|
adjMM = data[9U];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotStartVoiceFrame::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
// Sanity checks
|
||||||
|
assert(data != nullptr);
|
||||||
|
assert(startOfStream != nullptr);
|
||||||
|
assert(fullRateVoice != nullptr);
|
||||||
|
|
||||||
|
// Encode start of stream
|
||||||
|
if (startOfStream != nullptr) {
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
startOfStream->encode(buffer);
|
||||||
|
// Copy to data array (skipping first and last bytes)
|
||||||
|
::memcpy(data + 1U, buffer + 1U, startOfStream->LENGTH - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode full rate voice
|
||||||
|
if (fullRateVoice != nullptr) {
|
||||||
|
uint8_t buffer[fullRateVoice->SHORTENED_LENGTH];
|
||||||
|
fullRateVoice->encode(buffer, true);
|
||||||
|
data[0U] = fullRateVoice->frameType;
|
||||||
|
::memcpy(data + 10U, buffer + 1U, fullRateVoice->SHORTENED_LENGTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the rest
|
||||||
|
data[5U] = icw;
|
||||||
|
data[6U] = rssi;
|
||||||
|
data[7U] = rssiValidity;
|
||||||
|
data[8U] = nRssi;
|
||||||
|
data[9U] = adjMM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Motorola voice header 1
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MotVoiceHeader1::MotVoiceHeader1()
|
||||||
|
{
|
||||||
|
icw = ICW_DIU;
|
||||||
|
rssi = 0;
|
||||||
|
rssiValidity = INVALID;
|
||||||
|
nRssi = 0;
|
||||||
|
|
||||||
|
startOfStream = nullptr;
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotVoiceHeader1::MotVoiceHeader1(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotVoiceHeader1::~MotVoiceHeader1()
|
||||||
|
{
|
||||||
|
delete startOfStream;
|
||||||
|
delete[] header;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotVoiceHeader1::decode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Create a start of stream
|
||||||
|
startOfStream = new MotStartOfStream();
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
::memset(buffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
// We copy the bytes from [1:4]
|
||||||
|
::memcpy(buffer + 1U, data + 1U, 4);
|
||||||
|
startOfStream->decode(buffer);
|
||||||
|
|
||||||
|
// Decode the other stuff
|
||||||
|
icw = (ICWFlag)data[5U];
|
||||||
|
rssi = data[6U];
|
||||||
|
rssiValidity = (RssiValidityFlag)data[7U];
|
||||||
|
nRssi = data[8U];
|
||||||
|
|
||||||
|
// Our header includes the trailing source and check bytes
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
::memcpy(header, data + 9U, HCW_LENGTH);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotVoiceHeader1::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
assert(startOfStream != nullptr);
|
||||||
|
|
||||||
|
data[0U] = P25_DFSI_MOT_VHDR_1;
|
||||||
|
|
||||||
|
if (startOfStream != nullptr) {
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
::memset(buffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
startOfStream->encode(buffer);
|
||||||
|
// Copy the 4 start record bytes from the start of stream frame
|
||||||
|
::memcpy(data + 1U, buffer + 1U, 4U);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[5U] = icw;
|
||||||
|
data[6U] = rssi;
|
||||||
|
data[7U] = (uint8_t)rssiValidity;
|
||||||
|
data[8U] = nRssi;
|
||||||
|
|
||||||
|
// Our header includes the trailing source and check bytes
|
||||||
|
if (header != nullptr) {
|
||||||
|
::memcpy(data + 9U, header, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Motorola voice header 2
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MotVoiceHeader2::MotVoiceHeader2()
|
||||||
|
{
|
||||||
|
source = SOURCE_QUANTAR;
|
||||||
|
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotVoiceHeader2::MotVoiceHeader2(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotVoiceHeader2::~MotVoiceHeader2()
|
||||||
|
{
|
||||||
|
delete[] header;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotVoiceHeader2::decode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
source = (SourceFlag)data[21];
|
||||||
|
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
::memcpy(header, data + 1U, HCW_LENGTH);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotVoiceHeader2::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
data[0U] = P25_DFSI_MOT_VHDR_2;
|
||||||
|
|
||||||
|
if (header != nullptr) {
|
||||||
|
::memcpy(data + 1U, header, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[LENGTH - 1U] = (uint8_t)source;
|
||||||
|
}
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_RTP_FRAMES_H__)
|
||||||
|
#define __MOT_RTP_FRAMES_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
enum RTFlag {
|
||||||
|
ENABLED = 0x02U,
|
||||||
|
DISABLED = 0x04U
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StartStopFlag {
|
||||||
|
START = 0x0CU,
|
||||||
|
STOP = 0x25U
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StreamTypeFlag {
|
||||||
|
VOICE = 0x0BU
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RssiValidityFlag {
|
||||||
|
INVALID = 0x00U,
|
||||||
|
VALID = 0x1A
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SourceFlag {
|
||||||
|
SOURCE_DIU = 0x00U,
|
||||||
|
SOURCE_QUANTAR = 0x02U
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ICWFlag {
|
||||||
|
ICW_DIU = 0x00U,
|
||||||
|
ICW_QUANTAR = 0x1B
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Classes for Motorola-specific DFSI Frames (aka "THE" manufacturer)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class MotFullRateVoice {
|
||||||
|
public:
|
||||||
|
// Frame information
|
||||||
|
static const uint8_t LENGTH = 17;
|
||||||
|
static const uint8_t SHORTENED_LENGTH = 13;
|
||||||
|
static const uint8_t ADDITIONAL_LENGTH = 4;
|
||||||
|
static const uint8_t IMBE_BUF_LEN = 11;
|
||||||
|
|
||||||
|
uint8_t frameType;
|
||||||
|
uint8_t* imbeData;
|
||||||
|
uint8_t* additionalData;
|
||||||
|
SourceFlag source;
|
||||||
|
|
||||||
|
MotFullRateVoice();
|
||||||
|
MotFullRateVoice(uint8_t* data);
|
||||||
|
~MotFullRateVoice();
|
||||||
|
|
||||||
|
uint32_t size();
|
||||||
|
bool decode(uint8_t* data, bool shortened = false);
|
||||||
|
void encode(uint8_t* data, bool shortened = false);
|
||||||
|
private:
|
||||||
|
bool isVoice1or2or10or11();
|
||||||
|
bool isVoice2or11();
|
||||||
|
bool isVoice9or18();
|
||||||
|
};
|
||||||
|
|
||||||
|
class MotStartOfStream {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 10;
|
||||||
|
static const uint8_t FIXED_MARKER = 0x02;
|
||||||
|
|
||||||
|
uint8_t marker = FIXED_MARKER;
|
||||||
|
|
||||||
|
RTFlag rt;
|
||||||
|
StartStopFlag startStop;
|
||||||
|
StreamTypeFlag streamType;
|
||||||
|
|
||||||
|
MotStartOfStream();
|
||||||
|
MotStartOfStream(uint8_t* data);
|
||||||
|
|
||||||
|
bool decode(uint8_t* data);
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MotStartVoiceFrame {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 22;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
uint8_t adjMM;
|
||||||
|
|
||||||
|
MotStartOfStream* startOfStream;
|
||||||
|
MotFullRateVoice* fullRateVoice;
|
||||||
|
|
||||||
|
MotStartVoiceFrame();
|
||||||
|
MotStartVoiceFrame(uint8_t* data);
|
||||||
|
~MotStartVoiceFrame();
|
||||||
|
|
||||||
|
bool decode(uint8_t* data);
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MotVoiceHeader1 {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 30;
|
||||||
|
static const uint8_t HCW_LENGTH = 21;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
|
||||||
|
uint8_t* header;
|
||||||
|
MotStartOfStream* startOfStream;
|
||||||
|
|
||||||
|
MotVoiceHeader1();
|
||||||
|
MotVoiceHeader1(uint8_t* data);
|
||||||
|
~MotVoiceHeader1();
|
||||||
|
|
||||||
|
bool decode(uint8_t* data);
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MotVoiceHeader2 {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 22;
|
||||||
|
static const uint8_t HCW_LENGTH = 20;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
MotStartOfStream startOfStream;
|
||||||
|
SourceFlag source;
|
||||||
|
|
||||||
|
uint8_t* header;
|
||||||
|
|
||||||
|
MotVoiceHeader2();
|
||||||
|
MotVoiceHeader2(uint8_t* data);
|
||||||
|
~MotVoiceHeader2();
|
||||||
|
|
||||||
|
bool decode(uint8_t* data);
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __MOT_RTP_FRAMES_H__
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotStartOfStream.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotStartOfStream class.
|
||||||
|
/// </summary>
|
||||||
|
MotStartOfStream::MotStartOfStream()
|
||||||
|
{
|
||||||
|
rt = DISABLED;
|
||||||
|
startStop = START;
|
||||||
|
streamType = VOICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotStartOfStream class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
MotStartOfStream::MotStartOfStream(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode a start of stream frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotStartOfStream::decode(const uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Get parameters
|
||||||
|
rt = (RTFlag)data[2U];
|
||||||
|
startStop = (StartStopFlag)data[3U];
|
||||||
|
streamType = (StreamTypeFlag)data[4U];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode a start of stream frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
void MotStartOfStream::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Copy data
|
||||||
|
data[0U] = P25_DFSI_MOT_START_STOP;
|
||||||
|
data[1U] = FIXED_MARKER;
|
||||||
|
data[2U] = rt;
|
||||||
|
data[3U] = startStop;
|
||||||
|
data[4U] = streamType;
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_START_OF_STREAM_H__)
|
||||||
|
#define __MOT_START_OF_STREAM_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "rtp/RtpDefines.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Implements a P25 Motorola start of stream packet.
|
||||||
|
//
|
||||||
|
// Byte 0 1 2 3
|
||||||
|
// Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Fixed Mark | RT Mode Flag | Start/Stop | Type Flag |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Reserved |
|
||||||
|
// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API MotStartOfStream {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 10;
|
||||||
|
static const uint8_t FIXED_MARKER = 0x02;
|
||||||
|
|
||||||
|
uint8_t marker = FIXED_MARKER;
|
||||||
|
|
||||||
|
RTFlag rt;
|
||||||
|
StartStopFlag startStop;
|
||||||
|
StreamTypeFlag streamType;
|
||||||
|
|
||||||
|
/// <summary>Initializes a copy instance of the MotStartOfStream class.</summary>
|
||||||
|
MotStartOfStream();
|
||||||
|
/// <summary>Initializes a copy instance of the MotStartOfStream class.</summary>
|
||||||
|
MotStartOfStream(uint8_t* data);
|
||||||
|
|
||||||
|
/// <summary>Decode a start of stream frame.</summary>
|
||||||
|
bool decode(const uint8_t* data);
|
||||||
|
/// <summary>Encode a start of stream frame.</summary>
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __MOT_START_OF_STREAM_H__
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotStartVoiceFrame.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotStartVoiceFrame class.
|
||||||
|
/// </summary>
|
||||||
|
MotStartVoiceFrame::MotStartVoiceFrame()
|
||||||
|
{
|
||||||
|
icw = ICW_DIU;
|
||||||
|
rssi = 0;
|
||||||
|
rssiValidity = INVALID;
|
||||||
|
nRssi = 0;
|
||||||
|
adjMM = 0;
|
||||||
|
|
||||||
|
startOfStream = nullptr;
|
||||||
|
fullRateVoice = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotStartVoiceFrame class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
MotStartVoiceFrame::MotStartVoiceFrame(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the MotStartVoiceFrame class.
|
||||||
|
/// </summary>
|
||||||
|
MotStartVoiceFrame::~MotStartVoiceFrame()
|
||||||
|
{
|
||||||
|
delete startOfStream;
|
||||||
|
delete fullRateVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode a start voice frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotStartVoiceFrame::decode(const uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Create a new startOfStream
|
||||||
|
startOfStream = new MotStartOfStream();
|
||||||
|
|
||||||
|
// Create a buffer to decode the start record skipping the 10th byte (adjMM)
|
||||||
|
uint8_t startBuffer[startOfStream->LENGTH];
|
||||||
|
::memset(startBuffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
::memcpy(startBuffer, data, 9U);
|
||||||
|
|
||||||
|
// Decode start of stream
|
||||||
|
startOfStream->decode(startBuffer);
|
||||||
|
|
||||||
|
// Decode the full rate voice frames
|
||||||
|
fullRateVoice = new MotFullRateVoice();
|
||||||
|
uint8_t voiceBuffer[fullRateVoice->SHORTENED_LENGTH];
|
||||||
|
::memset(voiceBuffer, 0x00U, fullRateVoice->SHORTENED_LENGTH);
|
||||||
|
voiceBuffer[0U] = data[0U];
|
||||||
|
::memcpy(voiceBuffer + 1U, data + 10U, fullRateVoice->SHORTENED_LENGTH - 1);
|
||||||
|
fullRateVoice->decode(voiceBuffer, true);
|
||||||
|
|
||||||
|
// Get rest of data
|
||||||
|
icw = (ICWFlag)data[5U];
|
||||||
|
rssi = data[6U];
|
||||||
|
rssiValidity = (RssiValidityFlag)data[7U];
|
||||||
|
nRssi = data[8U];
|
||||||
|
adjMM = data[9U];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode a start voice frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
void MotStartVoiceFrame::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
assert(startOfStream != nullptr);
|
||||||
|
assert(fullRateVoice != nullptr);
|
||||||
|
|
||||||
|
// Encode start of stream
|
||||||
|
if (startOfStream != nullptr) {
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
startOfStream->encode(buffer);
|
||||||
|
// Copy to data array (skipping first and last bytes)
|
||||||
|
::memcpy(data + 1U, buffer + 1U, startOfStream->LENGTH - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode full rate voice
|
||||||
|
if (fullRateVoice != nullptr) {
|
||||||
|
uint8_t buffer[fullRateVoice->SHORTENED_LENGTH];
|
||||||
|
fullRateVoice->encode(buffer, true);
|
||||||
|
data[0U] = fullRateVoice->frameType;
|
||||||
|
::memcpy(data + 10U, buffer + 1U, fullRateVoice->SHORTENED_LENGTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the rest
|
||||||
|
data[5U] = icw;
|
||||||
|
data[6U] = rssi;
|
||||||
|
data[7U] = rssiValidity;
|
||||||
|
data[8U] = nRssi;
|
||||||
|
data[9U] = adjMM;
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_START_VOICE_FRAME_H__)
|
||||||
|
#define __MOT_START_VOICE_FRAME_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "rtp/RtpDefines.h"
|
||||||
|
#include "rtp/MotStartOfStream.h"
|
||||||
|
#include "rtp/MotFullRateVoice.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Implements a P25 Motorola voice frame 1/10 start.
|
||||||
|
//
|
||||||
|
// Byte 0 1 2 3
|
||||||
|
// Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Encoded Motorola Start of Stream |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | ICW Flag ? | RSSI | RSSI Valid | RSSI |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Adj MM ? | Full Rate Voice Frame |
|
||||||
|
// +-+-+-+-+-+-+-+-+ +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// +=+=+=+=+=+=+=+=+
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API MotStartVoiceFrame {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 22;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
uint8_t adjMM;
|
||||||
|
|
||||||
|
MotStartOfStream* startOfStream;
|
||||||
|
MotFullRateVoice* fullRateVoice;
|
||||||
|
|
||||||
|
/// <summary>Initializes a copy instance of the MotStartVoiceFrame class.</summary>
|
||||||
|
MotStartVoiceFrame();
|
||||||
|
/// <summary>Initializes a copy instance of the MotStartVoiceFrame class.</summary>
|
||||||
|
MotStartVoiceFrame(uint8_t* data);
|
||||||
|
/// <summary>Finalizes a instance of the MotStartVoiceFrame class.</summary>
|
||||||
|
~MotStartVoiceFrame();
|
||||||
|
|
||||||
|
/// <summary>Decode a start voice frame.</summary>
|
||||||
|
bool decode(const uint8_t* data);
|
||||||
|
/// <summary>Encode a start voice frame.</summary>
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __MOT_START_VOICE_FRAME_H__
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotVoiceHeader1.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotVoiceHeader1 class.
|
||||||
|
/// </summary>
|
||||||
|
MotVoiceHeader1::MotVoiceHeader1()
|
||||||
|
{
|
||||||
|
icw = ICW_DIU;
|
||||||
|
rssi = 0;
|
||||||
|
rssiValidity = INVALID;
|
||||||
|
nRssi = 0;
|
||||||
|
|
||||||
|
startOfStream = nullptr;
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotVoiceHeader1 class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
MotVoiceHeader1::MotVoiceHeader1(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the MotVoiceHeader1 class.
|
||||||
|
/// </summary>
|
||||||
|
MotVoiceHeader1::~MotVoiceHeader1()
|
||||||
|
{
|
||||||
|
delete startOfStream;
|
||||||
|
delete[] header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode a voice header 1 frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotVoiceHeader1::decode(const uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
// Create a start of stream
|
||||||
|
startOfStream = new MotStartOfStream();
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
::memset(buffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
// We copy the bytes from [1:4]
|
||||||
|
::memcpy(buffer + 1U, data + 1U, 4);
|
||||||
|
startOfStream->decode(buffer);
|
||||||
|
|
||||||
|
// Decode the other stuff
|
||||||
|
icw = (ICWFlag)data[5U];
|
||||||
|
rssi = data[6U];
|
||||||
|
rssiValidity = (RssiValidityFlag)data[7U];
|
||||||
|
nRssi = data[8U];
|
||||||
|
|
||||||
|
// Our header includes the trailing source and check bytes
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
::memcpy(header, data + 9U, HCW_LENGTH);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode a voice header 1 frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
void MotVoiceHeader1::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
assert(startOfStream != nullptr);
|
||||||
|
|
||||||
|
data[0U] = P25_DFSI_MOT_VHDR_1;
|
||||||
|
|
||||||
|
if (startOfStream != nullptr) {
|
||||||
|
uint8_t buffer[startOfStream->LENGTH];
|
||||||
|
::memset(buffer, 0x00U, startOfStream->LENGTH);
|
||||||
|
startOfStream->encode(buffer);
|
||||||
|
// Copy the 4 start record bytes from the start of stream frame
|
||||||
|
::memcpy(data + 1U, buffer + 1U, 4U);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[5U] = icw;
|
||||||
|
data[6U] = rssi;
|
||||||
|
data[7U] = (uint8_t)rssiValidity;
|
||||||
|
data[8U] = nRssi;
|
||||||
|
|
||||||
|
// Our header includes the trailing source and check bytes
|
||||||
|
if (header != nullptr) {
|
||||||
|
::memcpy(data + 9U, header, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_VOICE_HEADER_1_H__)
|
||||||
|
#define __MOT_VOICE_HEADER_1_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "rtp/RtpDefines.h"
|
||||||
|
#include "rtp/MotStartOfStream.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Implements a P25 Motorola voice header frame 1.
|
||||||
|
//
|
||||||
|
// Byte 0 1 2 3
|
||||||
|
// Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Encoded Motorola Start of Stream |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | ICW Flag ? | RSSI | RSSI Valid | RSSI |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Header Control Word |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Src Flag |
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API MotVoiceHeader1 {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 30;
|
||||||
|
static const uint8_t HCW_LENGTH = 21;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
|
||||||
|
uint8_t* header;
|
||||||
|
MotStartOfStream* startOfStream;
|
||||||
|
|
||||||
|
/// <summary>Initializes a copy instance of the MotVoiceHeader1 class.</summary>
|
||||||
|
MotVoiceHeader1();
|
||||||
|
/// <summary>Initializes a copy instance of the MotVoiceHeader1 class.</summary>
|
||||||
|
MotVoiceHeader1(uint8_t* data);
|
||||||
|
/// <summary>Finalizes a instance of the MotVoiceHeader1 class.</summary>
|
||||||
|
~MotVoiceHeader1();
|
||||||
|
|
||||||
|
/// <summary>Decode a voice header 1 frame.</summary>
|
||||||
|
bool decode(const uint8_t* data);
|
||||||
|
/// <summary>Encode a voice header 1 frame.</summary>
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __MOT_VOICE_HEADER_1_H__
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rtp/MotVoiceHeader2.h"
|
||||||
|
#include "common/p25/dfsi/DFSIDefines.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace p25;
|
||||||
|
using namespace dfsi;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotVoiceHeader2 class.
|
||||||
|
/// </summary>
|
||||||
|
MotVoiceHeader2::MotVoiceHeader2()
|
||||||
|
{
|
||||||
|
source = SOURCE_QUANTAR;
|
||||||
|
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a instance of the MotVoiceHeader2 class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
MotVoiceHeader2::MotVoiceHeader2(uint8_t* data)
|
||||||
|
{
|
||||||
|
decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the MotVoiceHeader2 class.
|
||||||
|
/// </summary>
|
||||||
|
MotVoiceHeader2::~MotVoiceHeader2()
|
||||||
|
{
|
||||||
|
delete[] header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode a voice header 2 frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool MotVoiceHeader2::decode(const uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
source = (SourceFlag)data[21];
|
||||||
|
|
||||||
|
header = new uint8_t[HCW_LENGTH];
|
||||||
|
::memset(header, 0x00U, HCW_LENGTH);
|
||||||
|
::memcpy(header, data + 1U, HCW_LENGTH);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encode a voice header 2 frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
void MotVoiceHeader2::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
data[0U] = P25_DFSI_MOT_VHDR_2;
|
||||||
|
|
||||||
|
if (header != nullptr) {
|
||||||
|
::memcpy(data + 1U, header, HCW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[LENGTH - 1U] = (uint8_t)source;
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__MOT_VOICE_HEADER_2_H__)
|
||||||
|
#define __MOT_VOICE_HEADER_2_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "rtp/RtpDefines.h"
|
||||||
|
#include "rtp/MotStartOfStream.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Implements a P25 Motorola voice header frame 2.
|
||||||
|
//
|
||||||
|
// Byte 0 1 2 3
|
||||||
|
// Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Header Control Word |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// | |
|
||||||
|
// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | | Reserved |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API MotVoiceHeader2 {
|
||||||
|
public:
|
||||||
|
static const uint8_t LENGTH = 22;
|
||||||
|
static const uint8_t HCW_LENGTH = 20;
|
||||||
|
|
||||||
|
ICWFlag icw;
|
||||||
|
uint8_t rssi;
|
||||||
|
RssiValidityFlag rssiValidity;
|
||||||
|
uint8_t nRssi;
|
||||||
|
MotStartOfStream startOfStream;
|
||||||
|
SourceFlag source;
|
||||||
|
|
||||||
|
uint8_t* header;
|
||||||
|
|
||||||
|
/// <summary>Initializes a copy instance of the MotVoiceHeader2 class.</summary>
|
||||||
|
MotVoiceHeader2();
|
||||||
|
/// <summary>Initializes a copy instance of the MotVoiceHeader2 class.</summary>
|
||||||
|
MotVoiceHeader2(uint8_t* data);
|
||||||
|
/// <summary>Finalizes a instance of the MotVoiceHeader2 class.</summary>
|
||||||
|
~MotVoiceHeader2();
|
||||||
|
|
||||||
|
/// <summary>Decode a voice header 2 frame.</summary>
|
||||||
|
bool decode(const uint8_t* data);
|
||||||
|
/// <summary>Encode a voice header 2 frame.</summary>
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __MOT_VOICE_HEADER_2_H__
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__RTP_DEFINES_H__)
|
||||||
|
#define __RTP_DEFINES_H__
|
||||||
|
|
||||||
|
#include "common/Defines.h"
|
||||||
|
|
||||||
|
namespace p25
|
||||||
|
{
|
||||||
|
namespace dfsi
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum RTFlag {
|
||||||
|
ENABLED = 0x02U,
|
||||||
|
DISABLED = 0x04U
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum StartStopFlag {
|
||||||
|
START = 0x0CU,
|
||||||
|
STOP = 0x25U
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum StreamTypeFlag {
|
||||||
|
VOICE = 0x0BU
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum RssiValidityFlag {
|
||||||
|
INVALID = 0x00U,
|
||||||
|
VALID = 0x1A
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum SourceFlag {
|
||||||
|
SOURCE_DIU = 0x00U,
|
||||||
|
SOURCE_QUANTAR = 0x02U
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
enum ICWFlag {
|
||||||
|
ICW_DIU = 0x00U,
|
||||||
|
ICW_QUANTAR = 0x1B
|
||||||
|
};
|
||||||
|
} // namespace dfsi
|
||||||
|
} // namespace p25
|
||||||
|
|
||||||
|
#endif // __RTP_DEFINES_H__
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @package DVM / DFSI peer application
|
||||||
|
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
|
||||||
|
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Patrick McDonnell, W3AXL
|
||||||
|
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if !defined(__RTP_FRAMES_H__)
|
||||||
|
#define __RTP_FRAMES_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include "rtp/MotFullRateVoice.h"
|
||||||
|
#include "rtp/MotStartOfStream.h"
|
||||||
|
#include "rtp/MotStartVoiceFrame.h"
|
||||||
|
#include "rtp/MotVoiceHeader1.h"
|
||||||
|
#include "rtp/MotVoiceHeader2.h"
|
||||||
|
|
||||||
|
#endif // __RTP_FRAMES_H__
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 8d6611ef703abd74d08bce2afe46d0e09676383a
|
Subproject commit 882244795b9bec1f2c86d596d4ab4ecd4346c8b9
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit d79b1d7b8d4b175be86cd0e5dc844c6ca5ae6733
|
Subproject commit a9ac06bc01309355b0da3530a5009c2c42cf5de5
|
||||||
Loading…
Reference in new issue