drop dvmdfsi as a standalone application (this functionality has been entirely rolled into dvmhost); update README.md;

pull/65/head
Bryan Biedenkapp 2 years ago
parent 617c889d1a
commit ec1cf8c87e

@ -227,7 +227,6 @@ endif (ENABLE_TESTS)
install(TARGETS dvmhost DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS dvmcmd DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS dvmfne DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
install(TARGETS dvmdfsi DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
install(TARGETS dvmmon DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
@ -249,14 +248,12 @@ if (NOT TARGET strip)
COMMAND arm-linux-gnueabihf-strip -s dvmhost
COMMAND arm-linux-gnueabihf-strip -s dvmfne
COMMAND arm-linux-gnueabihf-strip -s dvmcmd
COMMAND arm-linux-gnueabihf-strip -s dvmmon
COMMAND arm-linux-gnueabihf-strip -s dvmdfsi)
COMMAND arm-linux-gnueabihf-strip -s dvmmon)
else()
add_custom_target(strip
COMMAND arm-linux-gnueabihf-strip -s dvmhost
COMMAND arm-linux-gnueabihf-strip -s dvmfne
COMMAND arm-linux-gnueabihf-strip -s dvmcmd
COMMAND arm-linux-gnueabihf-strip -s dvmdfsi)
COMMAND arm-linux-gnueabihf-strip -s dvmcmd)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
elseif (CROSS_COMPILE_AARCH64)
if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
@ -264,28 +261,24 @@ if (NOT TARGET strip)
COMMAND aarch64-linux-gnu-strip -s dvmhost
COMMAND aarch64-linux-gnu-strip -s dvmfne
COMMAND aarch64-linux-gnu-strip -s dvmcmd
COMMAND aarch64-linux-gnu-strip -s dvmmon
COMMAND aarch64-linux-gnu-strip -s dvmdfsi)
COMMAND aarch64-linux-gnu-strip -s dvmmon)
else()
add_custom_target(strip
COMMAND aarch64-linux-gnu-strip -s dvmhost
COMMAND aarch64-linux-gnu-strip -s dvmfne
COMMAND aarch64-linux-gnu-strip -s dvmcmd
COMMAND aarch64-linux-gnu-strip -s dvmdfsi)
COMMAND aarch64-linux-gnu-strip -s dvmcmd)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
elseif (CROSS_COMPILE_RPI_ARM)
if (NOT WITH_RPI_ARM_TOOLS)
add_custom_target(strip
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmdfsi)
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd)
else()
add_custom_target(strip
COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost
COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne
COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd
COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmdfsi)
COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd)
endif ()
else()
if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
@ -293,14 +286,12 @@ if (NOT TARGET strip)
COMMAND strip -s dvmhost
COMMAND strip -s dvmfne
COMMAND strip -s dvmcmd
COMMAND strip -s dvmmon
COMMAND strip -s dvmdfsi)
COMMAND strip -s dvmmon)
else()
add_custom_target(strip
COMMAND strip -s dvmhost
COMMAND strip -s dvmfne
COMMAND strip -s dvmcmd
COMMAND strip -s dvmdfsi)
COMMAND strip -s dvmcmd)
endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR))
endif (CROSS_COMPILE_ARM)
endif (NOT TARGET strip)
@ -326,7 +317,6 @@ if (NOT TARGET tarball)
COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmmon ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmdfsi ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp ../tools/*.sh ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm
COMMAND chmod +x ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/*.sh
COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm
@ -355,7 +345,6 @@ if (NOT TARGET tarball)
COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp -v dvmdfsi ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin
COMMAND cp ../tools/*.sh ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm
COMMAND chmod +x ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/*.sh
COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm
@ -394,7 +383,6 @@ add_custom_target(old_install
COMMAND install -m 755 dvmcmd ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 dvmmon ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 dvmfne ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 755 dvmdfsi ${CMAKE_LEGACY_INSTALL_PREFIX}/bin
COMMAND install -m 644 ../configs/config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/config.example.yml
COMMAND install -m 644 ../configs/fne-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-config.example.yml
COMMAND install -m 644 ../configs/monitor-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/monitor-config.example.yml

@ -1,12 +1,11 @@
# Digital Voice Modem Host
The DVM Host (dvmhost) software suite, provides the a set of applications that, act as a primary host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual modem hardware, a TIA/V.24 standard interface (dvmdfsi) allowing communications to commercial P25 hardware, and the networking core (dvmfne) that provides a centralized network service that interconnects various DVM endpoint applications allowing networked communications.
The DVM Host (dvmhost) software suite, provides the a set of applications that, act as a primary host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual air modem hardware, a TIA/V.24 standard interface mode allowing communications to commercial P25 hardware using the V.24 DFSI modem hardware or UDP, and the networking core (dvmfne) that provides a centralized network service that interconnects various DVM endpoint applications allowing networked communications.
Please feel free to reach out to us for help, comments or otherwise, on our Discord: https://discord.gg/3pBe8xgrEz
This project generates a few executables:
- `dvmhost` host software that connects to the DVM modems (both repeater and hotspot) and is the primary data processing application for digital modes. [See configuration](#dvmhost-configuration) to configure and calibrate.
- `dvmdfsi` TIA/V.24 standard interface application that connects to a V.24 interface board or UDP to allow for P25 DFSI communications with commercial P25 hardware.
- `dvmfne` a network "core", this provides a central server for `dvmhost` instances to connect to and be networked with, allowing relay of traffic and other data between `dvmhost` instances and other `dvmfne` instances. [See configuration](#dvmfne-configuration) to configure.
- `dvmcmd` a simple command-line utility to send remote control commands to a `dvmhost` or `dvmfne` instance with REST API configured.
- `dvmmon` a TUI utility that allows semi-realtime console-based monitoring of `dvmhost` instances (this tool is only available when project wide TUI support is enabled!).
@ -80,9 +79,12 @@ It should also be important to read and review the [calibration notes](#calibrat
### Initial Setup Steps
The following setups assume the host is compiled with the setup TUI mode (if availble). It is possible to setup the modem without the setup TUI, and requires manually modifying `config.yml` and the `iden_table.dat` files.
The following setups assume the host is compiled with the setup TUI mode (if available) [NOTE: Steps 3 - 5 only apply to the air interface modem.]. It is possible to setup the modem without the setup TUI, and requires manually modifying `config.yml` and the `iden_table.dat` files.
1. Create/Edit `config.yml` and ensure the settings for the modem are correct, find the "modem" section in "system". Check that the uart protocol has the appropriate UART port and port speed set (the config.yml defaults to /dev/ttyUSB0 and 115200).
1. Create/Edit `config.yml` and ensure the settings for the modem are correct, find the "modem" section in "system". Check that the uart settings have the appropriate UART port and port speed set (the config.yml defaults to /dev/ttyUSB0 and 115200).
1.1. If using the air modem interface, ensure the the modem protocol mode is set to "air".
1.2. If using the V.24 DFSI modem interface, ensure the modem protocol mode is set to "dfsi".
1.2.1. The V.24 DFSI modem has multiple firmware revisions, it is required to use firmware version 2.0 or greater for use with dvmhost.
2. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode.
3. Using the TUI user interface, use the "Setup" menu to set default parameters.
3.1. The "Logging & Data Configuration" submenu allows you to alter the various logging file paths and levels, as well as paths to data files (such as the `iden_table.dat` file).
@ -92,7 +94,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava
4. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration.
5. Quit setup mode (some settings changes require a restart of the software to be effective) using, "File" menu, "Quit".
### Transmit Calibration (using setup TUI, if available)
### (Air Interface) Transmit Calibration (using setup TUI, if available)
1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Bit Error Rate" functions under Performance Testing).
2. Depending on which protocol you are calibration with, use the "Calibrate" menu, and select the appropriate mode using the "Operational Mode" submenu. (For example, select [Tx] DMR BS 1031 Hz Test Pattern for DMR or [Tx] P25 1011 Hz Test Pattern (NAC293 ID1 TG1) for P25.)
@ -105,7 +107,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava
9. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration.
10. Quit setup mode, if done doing calibration, using, "File" menu, "Quit".
### Transmit Calibration (using old calibration CLI)
### (Air Interface) Transmit Calibration (using old calibration CLI)
1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --cal`. This will start the dvmhost calibration mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Bit Error Rate" functions under Performance Testing).
2. Depending on which protocol you are calibration with, enter DMR BS 1031 Hz Test Pattern (M) or P25 1011 Hz Test Pattern (NAC293 ID1 TG1) (P).
@ -116,7 +118,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava
7. Stop Tx (press spacebar to toggle Tx).
8. Save the configuration using "s" and quit calibration mode with "q".
### Receive Calibration (using setup TUI, if available)
### (Air Interface) Receive Calibration (using setup TUI, if available)
1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Transmitter Test Pattern" functions under Performance Testing).
2. Depending on which protocol you are calibration with, use the "Calibrate" menu, and select the appropriate mode using the "Operational Mode" submenu. (For example, select [Rx] DMR BS 1031 Hz Test Pattern for DMR or [Rx] P25 1011 Hz Test Pattern (NAC293 ID1 TG1) for P25.)
@ -127,7 +129,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava
8. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration.
9. Quit setup mode, if done doing calibration, using, "File" menu, "Quit".
### Receive Calibration (using old calibration CLI)
### (Air Interface) Receive Calibration (using old calibration CLI)
1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --cal`. This will start the dvmhost calibration mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Transmitter Test Pattern" functions under Performance Testing).
2. Depending on which protocol you are calibration with, enter DMR BS 1031 Hz Test Pattern (M) or P25 1011 Hz Test Pattern (P).
@ -137,7 +139,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava
6. While observing the BER via the calibration console, adjust the RX potentiometer(s) for the lowest received BER. If necessary also adjust the software RXLevel for some fine tuning with the "R" (increase) and "r" (decrease).
7. Save the configuration using "s" and quit calibration mode with "q".
### Calibration Notes
### (Air Interface) Calibration Notes
- If you have access to appropriate RF test equipment (or equivilant equipment) that is capable of monitor the overall transmitted *analog* FM deviation; if is important to adjust both the modem and the connected radios so that the overall transmitted *analog* FM deviation be between 2.75khz and 2.83khz (a center average of 2.80khz *analog* FM deviation is best).
- When using a repeater/modem board attached to an appropriate FM repeater/radio, it *may* be necessary to "de-tune" the repeater/radio slightly, most commercial grade equipment operating within a 12.5khz channel may impose a strict 2.5khz (and no greater) maximum *analog* FM deviation, this is well below what is required for good digital operation. It may be necessary using whatever tuning/alignment tools to "de-tune" or adjust the equipments alignment to allow for a wider *analog* FM deviation, as close to 2.80khz as possible.
@ -176,21 +178,6 @@ usage: ./dvmhost [-vhdf][--syslog][--setup][-c <configuration file>][--remote [-
-- stop handling options
```
### dvmdfsi Command Line Parameters
```
usage: ./dvmdfsi [-vhf][--syslog][-c <configuration file>]
-v show version information
-h show this screen
-f foreground mode
--syslog force logging to syslog
-c <file> specifies the configuration file to use
-- stop handling options
```
### dvmfne Command Line Parameters
```

@ -1,105 +0,0 @@
#
# Digital Voice Modem - DFSI Software Configuration
#
# @package DVM / DFSI Software
#
# Flag indicating whether the host will run as a background or foreground task.
daemon: false
#
# Network Configuration
#
network:
# Textual Name
identity: DFSI
# Network Peer ID
peerId: 9000123
# Hostname/IP address of FNE master to connect to.
address: 127.0.0.1
# Port number to connect to.
port: 62031
# FNE access password.
password: RPT1234
# Additional debug logging
debug: false
# Encrypted endpoint networking
encrypted: false
# Pre-shared key for encrypted networking (AES-256 32-byte hex keystring)
presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
#
# DFSI Configuration
#
dfsi:
# Mode
# 1 - UDP DFSI to DVM FNE (TODO: Implement this)
# 2 - Serial DFSI to DVM FNE (Using DVM-V24 adapter board)
# 3 - Serial DFSI to UDP DFSI (TODO: Implement this)
mode: 2
# Flag enabling "the" Manufacturer standard of RTP.
theManufacturer: false
# P25 buffer size in bytes (don't change this unless you have a good reason to)
# Default of 3060 is 10 full LDU1/2s worth of data
p25BufferSize: 3060
udp:
# Time in seconds between heartbets to DFSI peers.
heartbeat: 5
# Flag disabling control connection establishment.
noConnectionEstablishment: false
# Local DFSI RTP Port number.
localRtpPort: 27500
# Remote RFSS DFSI Hostname/IP address of FNE master to connect to.
remoteDfsiAddress: 127.0.0.2
# Remote DFSI Control Port number to connect to.
remoteControlPort: 27000
# Remote DFSI RTP Port number to connect to.
remoteRtpPort: 27500
serial:
# Modem port type.
portType: "uart" # Valid values are "null", and "uart"
# Serial configuration for serial DFSI
port: "/dev/ttyACM0"
#
baudrate: 115200
# RT/RT flag enabled (0x02) or disabled (0x04)
rtrt: true
# Use the DIU source flag (0x00) instead of the quantar source flag (0x02)
diu: true
# Jitter buffer length in ms
jitter: 200
# Debug logging
debug: true
# Trace logging (prints lots of data)
trace: false
# Timer which will reset local/remote call flags if frames aren't received longer than this time in ms
callTimeout: 200
#
# Logging Configuration
# Logging Levels:
# 1 - Debug
# 2 - Message
# 3 - Informational
# 4 - Warning
# 5 - Error
# 6 - Fatal
#
log:
# Console display logging level (used when in foreground).
displayLevel: 1
# File logging level.
fileLevel: 1
# Flag indicating file logs should be sent to syslog instead of a file.
useSyslog: false
# Full path for the directory to store the log files.
filePath: .
# Full path for the directory to store the activity log files.
activityFilePath: .
# Log filename prefix.
fileRoot: dvmdfsi

@ -81,11 +81,3 @@ include(src/remote/CMakeLists.txt)
add_executable(dvmcmd ${common_INCLUDE} ${dvmcmd_SRC})
target_link_libraries(dvmcmd PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads)
target_include_directories(dvmcmd PRIVATE ${OPENSSL_INCLUDE_DIR} src src/remote)
#
## dvmdfsi
#
include(src/dfsi/CMakeLists.txt)
add_executable(dvmdfsi ${common_INCLUDE} ${dvmdfsi_SRC})
target_link_libraries(dvmdfsi PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads)
target_include_directories(dvmdfsi PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/dfsi)

@ -1,150 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
#include "ActivityLog.h"
#include "common/network/BaseNetwork.h"
#include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork()
#include <sys/time.h>
#if defined(CATCH2_TEST_COMPILATION)
#include <catch2/catch_test_macros.hpp>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define EOL "\r\n"
const uint32_t ACT_LOG_BUFFER_LEN = 501U;
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------
static std::string m_actFilePath;
static std::string m_actFileRoot;
static FILE* m_actFpLog = nullptr;
static struct tm m_actTm;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
/* Helper to open the activity log file, file handle. */
static bool ActivityLogOpen()
{
if (CurrentLogFileLevel() == 0U)
return true;
time_t now;
::time(&now);
struct tm* tm = ::localtime(&now);
if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) {
if (m_actFpLog != nullptr)
return true;
}
else {
if (m_actFpLog != nullptr)
::fclose(m_actFpLog);
}
char filename[200U];
::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
m_actFpLog = ::fopen(filename, "a+t");
m_actTm = *tm;
return m_actFpLog != nullptr;
}
/* Initializes the activity log. */
bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot)
{
#if defined(CATCH2_TEST_COMPILATION)
return true;
#endif
m_actFilePath = filePath;
m_actFileRoot = fileRoot;
return ::ActivityLogOpen();
}
/* Finalizes the activity log. */
void ActivityLogFinalise()
{
#if defined(CATCH2_TEST_COMPILATION)
return;
#endif
if (m_actFpLog != nullptr)
::fclose(m_actFpLog);
}
/* Writes a new entry to the activity log. */
void ActivityLog(const char* msg, ...)
{
#if defined(CATCH2_TEST_COMPILATION)
return;
#endif
assert(msg != nullptr);
char buffer[ACT_LOG_BUFFER_LEN];
struct timeval now;
::gettimeofday(&now, NULL);
struct tm* tm = ::gmtime(&now.tv_sec);
::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U);
va_list vl, vl_len;
va_start(vl, msg);
va_copy(vl_len, vl);
size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len);
::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl);
va_end(vl_len);
va_end(vl);
bool ret = ::ActivityLogOpen();
if (!ret)
return;
if (LogGetNetwork() != nullptr) {
network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();;
network->writeActLog(buffer);
}
if (CurrentLogFileLevel() == 0U)
return;
::fprintf(m_actFpLog, "%s\n", buffer);
::fflush(m_actFpLog);
if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) {
::fprintf(stdout, "%s" EOL, buffer);
::fflush(stdout);
}
}

@ -1,45 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @file ActivityLog.h
* @ingroup dfsi
* @file ActivityLog.cpp
* @ingroup dfsi
*/
#if !defined(__ACTIVITY_LOG_H__)
#define __ACTIVITY_LOG_H__
#include "Defines.h"
#include <string>
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
/**
* @brief Initializes the activity log.
* @param filePath File path for the log file.
* @param fileRoot Root name for log file.
*/
extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot);
/**
* @brief Finalizes the activity log.
*/
extern HOST_SW_API void ActivityLogFinalise();
/**
* @brief Writes a new entry to the activity log.
* @param msg String format.
*
* This is a variable argument function.
*/
extern HOST_SW_API void ActivityLog(const char* msg, ...);
#endif // __ACTIVITY_LOG_H__

@ -1,23 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#/*
# * Digital Voice Modem - Modem Host Software
# * GPLv2 Open Source. Use is subject to license terms.
# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# *
# * Copyright (C) 2024 Patrick McDonnell, W3AXL
# *
# */
file(GLOB dvmdfsi_SRC
"src/host/modem/port/*.h"
"src/host/modem/port/*.cpp"
"src/host/network/Network.h"
"src/host/network/Network.cpp"
# Network Core
"src/dfsi/network/*.h"
"src/dfsi/network/*.cpp"
# Core
"src/dfsi/*.h"
"src/dfsi/*.cpp"
)

@ -1,45 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @defgroup dfsi DFSI V.24/UDP Software (dvmdfsi)
* @brief Digital Voice Modem - DFSI V.24/UDP Software
* @details TIA/V.24 standard interface application that connects to a V.24 interface board or UDP to allow for P25 DFSI communications with commercial P25 hardware.
* @ingroup dfsi
*
* @file Defines.h
* @ingroup dfsi
*/
#if !defined(__DEFINES_H__)
#define __DEFINES_H__
#include "common/Defines.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#undef __PROG_NAME__
#define __PROG_NAME__ "Digital Voice Modem (DVM) DFSI V.24/UDP Peer"
#undef __EXE_NAME__
#define __EXE_NAME__ "dvmdfsi"
#undef __NETVER__
#define __NETVER__ "DFSI" VERSION_MAJOR VERSION_REV VERSION_MINOR
#undef DEFAULT_CONF_FILE
#define DEFAULT_CONF_FILE "dfsi-config.yml"
#undef DEFAULT_LOCK_FILE
#define DEFAULT_LOCK_FILE "/tmp/dvmdfsi.lock"
#define DFSI_MODE_UDP_FNE 1
#define DFSI_MODE_V24_FNE 2
#define DFSI_MODE_UDP_V24 3
#endif // __DEFINES_H__

@ -1,418 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "common/dmr/DMRDefines.h"
#include "common/p25/P25Utils.h"
#include "common/network/udp/Socket.h"
#include "common/Log.h"
#include "common/StopWatch.h"
#include "common/Thread.h"
#include "common/Utils.h"
#include "host/ActivityLog.h"
#include "Dfsi.h"
#include "DfsiMain.h"
using namespace network;
using namespace lookups;
#include <cstdio>
#include <algorithm>
#include <functional>
#include <random>
#include <sys/utsname.h>
#include <unistd.h>
#include <pwd.h>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define IDLE_WARMUP_MS 5U
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the HostTest class. */
Dfsi::Dfsi(const std::string& confFile) :
m_confFile(confFile),
m_conf(),
m_network(nullptr),
m_ridLookup(nullptr),
m_tidLookup(nullptr),
m_pingTime(5U),
m_maxMissedPings(5U),
m_updateLookupTime(10U),
m_debug(false),
m_repeatTraffic(true),
m_serial(nullptr)
{
/* stub */
}
/* Finalizes a instance of the HostTest class. */
Dfsi::~Dfsi() = default;
/* Executes the main FNE processing loop. */
int Dfsi::run()
{
bool ret = false;
// Try and parse the config yaml
try {
ret = yaml::Parse(m_conf, m_confFile.c_str());
if (!ret) {
::fatal("cannot read the configuration file, %s\n", m_confFile.c_str());
}
}
catch (yaml::OperationException const& e) {
::fatal("cannot read the configuration file - %s (%s)", m_confFile.c_str(), e.message());
}
// Check if we should run as a daemn or not
bool m_daemon = m_conf["daemon"].as<bool>(false);
if (m_daemon && g_foreground)
m_daemon = false;
// initialize system logging
yaml::Node logConf = m_conf["log"];
ret = ::LogInitialise(logConf["filePath"].as<std::string>(), logConf["fileRoot"].as<std::string>(),
logConf["fileLevel"].as<uint32_t>(0U), logConf["displayLevel"].as<uint32_t>(0U), false, logConf["useSyslog"].as<bool>(false));
if (!ret) {
::fatal("unable to open the log file\n");
}
// Init activity logging
ret = ::ActivityLogInitialise(logConf["activityFilePath"].as<std::string>(), logConf["fileRoot"].as<std::string>());
if (!ret) {
::fatal("unable to open the activity log file\n");
}
// handle POSIX process forking
if (m_daemon) {
// create new process
pid_t pid = ::fork();
if (pid == -1) {
::fprintf(stderr, "%s: Couldn't fork() , exiting\n", g_progExe.c_str());
::LogFinalise();
return EXIT_FAILURE;
}
else if (pid != 0) {
::LogFinalise();
exit(EXIT_SUCCESS);
}
// create new session and process group
if (::setsid() == -1) {
::fprintf(stderr, "%s: Couldn't setsid(), exiting\n", g_progExe.c_str());
::LogFinalise();
return EXIT_FAILURE;
}
// set the working directory to the root directory
if (::chdir("/") == -1) {
::fprintf(stderr, "%s: Couldn't cd /, exiting\n", g_progExe.c_str());
::LogFinalise();
return EXIT_FAILURE;
}
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
}
::LogInfo(__BANNER__ "\r\n" __PROG_NAME__ " " __VER__ " (built " __BUILD__ ")\r\n" \
"Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\r\n" \
">> DFSI Network Peer\r\n");
// read base parameters from configuration
ret = readParams();
if (!ret)
return EXIT_FAILURE;
// Read DFSI config
yaml::Node dfsi_conf = m_conf["dfsi"];
uint16_t dfsiMode = dfsi_conf["mode"].as<uint16_t>();
uint32_t p25BufferSize = dfsi_conf["p25BufferSize"].as<uint32_t>();
uint16_t callTimeout = dfsi_conf["callTimeout"].as<uint16_t>();
// initialize peer networking
ret = createPeerNetwork();
if (!ret)
return EXIT_FAILURE;
std::string dfsiModeStr = "Unknown";
switch (dfsiMode) {
case DFSI_MODE_UDP_FNE:
{
dfsiModeStr = "UDP DFSI to FNE";
LogError(LOG_HOST, "UDP DFSI mode not yet supported, sorry!");
return EXIT_FAILURE;
}
break;
case DFSI_MODE_V24_FNE:
{
dfsiModeStr = "V24 DFSI to FNE";
ret = createSerialNetwork(p25BufferSize, callTimeout);
if (!ret)
return EXIT_FAILURE;
}
break;
case DFSI_MODE_UDP_V24:
{
dfsiModeStr = "UDP DFSI to V24 DFSI";
LogError(LOG_HOST, "UDP to V24 mode not yet supported, sorry!");
return EXIT_FAILURE;
}
break;
default:
{
LogError(LOG_HOST, "Invalid DFSI mode specified: %d", dfsiMode);
return EXIT_FAILURE;
}
break;
}
LogInfo("DFSI Parameters");
LogInfo(" Mode: %u (%s)", dfsiMode, dfsiModeStr.c_str());
LogInfo(" P25 Buffer Size: %u bytes", p25BufferSize);
LogInfo(" Call Timeout: %u ms", callTimeout);
StopWatch stopWatch;
stopWatch.start();
/*
** Main execution loop
*/
struct utsname utsinfo;
::memset(&utsinfo, 0, sizeof(utsinfo));
::uname(&utsinfo);
::LogInfoEx(LOG_HOST, "[ OK ] DFSI is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine);
// Get serial board info if connected
if (m_serial)
{
m_serial->getBoardInfo();
}
// main execution loop
while (!g_killed) {
uint32_t ms = stopWatch.elapsed();
ms = stopWatch.elapsed();
stopWatch.start();
// ------------------------------------------------------
// -- Network RX Clocking --
// ------------------------------------------------------
if (m_network != nullptr)
m_network->clock(ms);
uint32_t length = 0U;
bool netReadRet = false;
UInt8Array p25Buffer = m_network->readP25(netReadRet, length);
if (netReadRet) {
// Send the data to the serial handler if serial is up
if (m_serial != nullptr)
m_serial->processP25FromNet(std::move(p25Buffer), length);
}
// ------------------------------------------------------
// -- Network TX Clocking --
// ------------------------------------------------------
// Processes data in the serial rx P25 buffer and sends it to the network buffer for sending, if serial is up
if (m_serial != nullptr) {
m_serial->processP25ToNet();
}
// ------------------------------------------------------
// -- Serial Clocking --
// ------------------------------------------------------
if (m_serial != nullptr) {
m_serial->clock(ms);
}
// Timekeeping
if (ms < 2U)
Thread::sleep(1U);
}
::LogSetNetwork(nullptr);
if (m_network != nullptr) {
m_network->close();
delete m_network;
}
if (m_serial != nullptr) {
m_serial->close();
delete m_serial;
}
if (m_tidLookup != nullptr) {
m_tidLookup->stop();
delete m_tidLookup;
}
if (m_ridLookup != nullptr) {
m_ridLookup->stop();
delete m_ridLookup;
}
return EXIT_SUCCESS;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/* Reads basic configuration parameters from the YAML configuration file. */
bool Dfsi::readParams()
{
// No basic config params right now
return true;
}
/* Initializes peer network connectivity. */
bool Dfsi::createPeerNetwork()
{
yaml::Node networkConf = m_conf["network"];
std::string password = networkConf["password"].as<std::string>();
std::string address = networkConf["address"].as<std::string>();
uint16_t port = networkConf["port"].as<uint16_t>();
uint32_t id = networkConf["peerId"].as<uint32_t>();
bool encrypted = networkConf["encrypted"].as<bool>(false);
std::string key = networkConf["presharedKey"].as<std::string>();
uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
if (!key.empty()) {
if (key.size() == 32) {
// bryanb: shhhhhhh....dirty nasty hacks
key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs)
LogWarning(LOG_HOST, "Half-length network preshared encryption key detected, doubling key on itself.");
}
if (key.size() == 64) {
if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
const char* keyPtr = key.c_str();
::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) {
char t[4] = {keyPtr[0], keyPtr[1], 0};
presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16);
keyPtr += 2 * sizeof(char);
}
}
else {
LogWarning(LOG_HOST, "Invalid characters in the network preshared encryption key. Encryption disabled.");
encrypted = false;
}
}
else {
LogWarning(LOG_HOST, "Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled.");
encrypted = false;
}
}
std::string identity = networkConf["identity"].as<std::string>();
bool netDebug = networkConf["debug"].as<bool>();
LogInfo("Network Parameters");
LogInfo(" Identity: %s", identity.c_str());
LogInfo(" Peer ID: %u", id);
LogInfo(" Address: %s", address.c_str());
LogInfo(" Port: %u", port);
LogInfo(" Encrypted: %s", encrypted ? "yes" : "no");
if (id > 999999999U) {
::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999.");
return false;
}
// initialize networking
m_network = new DfsiPeerNetwork(address, port, 0U, id, password, true, netDebug, false, true, false, true, true, true, true, true, false);
m_network->setMetadata(identity, 0U, 0U, 0.0F, 0.0F, 0, 0, 0, 0.0F, 0.0F, 0, "");
m_network->setLookups(m_ridLookup, m_tidLookup);
::LogSetNetwork(m_network);
if (encrypted) {
m_network->setPresharedKey(presharedKey);
}
m_network->enable(true);
bool ret = m_network->open();
if (!ret) {
delete m_network;
m_network = nullptr;
LogError(LOG_HOST, "failed to initialize traffic networking for PEER %u", id);
return false;
}
::LogSetNetwork(m_network);
return true;
}
/* Initializes serial V.24 network. */
bool Dfsi::createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout)
{
// Read serial config
yaml::Node dfsi_conf = m_conf["dfsi"];
yaml::Node serial_conf = dfsi_conf["serial"];
std::string portType = serial_conf["portType"].as<std::string>("null");
std::string port = serial_conf["port"].as<std::string>();
uint32_t baudrate = serial_conf["baudrate"].as<uint32_t>();
bool rtrt = serial_conf["rtrt"].as<bool>();
bool diu = serial_conf["diu"].as<bool>();
uint16_t jitter = serial_conf["jitter"].as<uint16_t>();
bool serial_debug = serial_conf["debug"].as<bool>();
bool serial_trace = serial_conf["trace"].as<bool>();
LogInfo("Serial Parameters");
LogInfo(" Port Type: %s", portType.c_str());
LogInfo(" Port: %s", port.c_str());
LogInfo(" Baudrate: %u", baudrate);
LogInfo(" RT/RT: %s", rtrt ? "Enabled" : "Disabled");
LogInfo(" DIU Flag: %s", diu ? "Enabled" : "Disabled");
LogInfo(" Jitter Size: %u ms", jitter);
LogInfo(" Debug: %s", serial_debug ? "Enabled" : "Disabled");
LogInfo(" Trace: %s", serial_trace ? "Enabled" : "Disabled");
// Create serial service
m_serial = new SerialService(portType, port, baudrate, rtrt, diu, jitter, m_network, p25BufferSize, p25BufferSize, callTimeout, serial_debug, serial_trace);
// Open serial
bool ret = m_serial->open();
if (!ret) {
delete m_serial;
m_serial = nullptr;
LogError(LOG_HOST, "failed to initialied serial V24 interface");
return false;
}
return true;
}

@ -1,94 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @file Dfsi.h
* @ingroup dfsi
* @file Dfsi.cpp
* @ingroup dfsi
*/
#if !defined(__DFSI_H__)
#define __DFSI_H__
#include "Defines.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/yaml/Yaml.h"
#include "common/Timer.h"
#include "network/DfsiPeerNetwork.h"
#include "network/SerialService.h"
#include <string>
#include <unordered_map>
#include <vector>
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief This class implements the core service logic.
* @ingroup dfsi
*/
class HOST_SW_API Dfsi {
public:
/**
* @brief Initializes a new instance of the HostTest class.
* @param confFile Full-path to the configuration file.
*/
Dfsi(const std::string& confFile);
/**
* @brief Finalizes a instance of the HostTest class.
*/
~Dfsi();
/**
* @brief Executes the main host processing loop.
* @returns int Zero if successful, otherwise error occurred.
*/
int run();
private:
const std::string& m_confFile;
yaml::Node m_conf;
network::DfsiPeerNetwork* m_network;
lookups::RadioIdLookup* m_ridLookup;
lookups::TalkgroupRulesLookup* m_tidLookup;
uint32_t m_pingTime;
uint32_t m_maxMissedPings;
uint32_t m_updateLookupTime;
bool m_debug;
bool m_repeatTraffic;
network::SerialService* m_serial;
/**
* @brief Reads basic configuration parameters from the INI.
* @returns bool True, if configuration was read successfully, otherwise false.
*/
bool readParams();
/**
* @brief Initializes peer network connectivity.
* @returns bool True, if network connectivity was initialized, otherwise false.
*/
bool createPeerNetwork();
/**
* @brief Initializes serial V.24 network.
* @returns bool True, if serial connectivity was initialized, otherwise false.
*/
bool createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout);
};
#endif // __DFSI_H__

@ -1,230 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "common/Log.h"
#include "dfsi/ActivityLog.h"
#include "DfsiMain.h"
#include "Dfsi.h"
using namespace network;
using namespace lookups;
#include <cstdio>
#include <cstdarg>
#include <vector>
#include <signal.h>
// ---------------------------------------------------------------------------
// Macros
// ---------------------------------------------------------------------------
#define IS(s) (::strcmp(argv[i], s) == 0)
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------
int g_signal = 0;
std::string g_progExe = std::string(__EXE_NAME__);
std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
std::string g_lockFile = std::string(DEFAULT_LOCK_FILE);
std::string g_masterAddress = std::string("127.0.0.1");
uint16_t g_masterPort = 63031;
uint32_t g_peerId = 9000999;
bool g_foreground = false;
bool g_killed = false;
bool g_hideMessages = false;
uint8_t* g_gitHashBytes = nullptr;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
#if !defined(CATCH2_TEST_COMPILATION)
/* Internal signal handler. */
static void sigHandler(int signum)
{
g_signal = signum;
g_killed = true;
}
#endif
/* Helper to print a fatal error message and exit. */
void fatal(const char* msg, ...)
{
char buffer[400U];
::memset(buffer, 0x20U, 400U);
va_list vl;
va_start(vl, msg);
::vsprintf(buffer, msg, vl);
va_end(vl);
::fprintf(stderr, "%s: %s\n", g_progExe.c_str(), buffer);
exit(EXIT_FAILURE);
}
/* Helper to pring usage the command line arguments. (And optionally an error.) */
void usage(const char* message, const char* arg)
{
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n\n");
if (message != nullptr) {
::fprintf(stderr, "%s: ", g_progExe.c_str());
::fprintf(stderr, message, arg);
::fprintf(stderr, "\n\n");
}
::fprintf(stdout,
"usage: %s [-vhf]"
"[--syslog]"
"[-c <configuration file>]"
"\n\n"
" -v show version information\n"
" -h show this screen\n"
" -f foreground mode\n"
"\n"
" --syslog force logging to syslog\n"
"\n"
" -c <file> specifies the configuration file to use\n"
"\n"
" -- stop handling options\n",
g_progExe.c_str());
exit(EXIT_FAILURE);
}
/* Helper to validate the command line arguments. */
int checkArgs(int argc, char* argv[])
{
int i, p = 0;
// iterate through arguments
for (i = 1; i <= argc; i++)
{
if (argv[i] == nullptr) {
break;
}
if (*argv[i] != '-') {
continue;
}
else if (IS("--")) {
++p;
break;
}
else if (IS("-f")) {
g_foreground = true;
}
else if (IS("--syslog")) {
g_useSyslog = true;
}
else if (IS("-c")) {
if (argc-- <= 0)
usage("error: %s", "must specify the configuration file to use");
g_iniFile = std::string(argv[++i]);
if (g_iniFile.empty())
usage("error: %s", "configuration file cannot be blank!");
p += 2;
}
else if (IS("-v")) {
::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__);
::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n");
::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n");
if (argc == 2)
exit(EXIT_SUCCESS);
}
else if (IS("-h")) {
usage(nullptr, nullptr);
if (argc == 2)
exit(EXIT_SUCCESS);
}
else {
usage("unrecognized option `%s'", argv[i]);
}
}
if (p < 0 || p > argc) {
p = 0;
}
return ++p;
}
// ---------------------------------------------------------------------------
// Program Entry Point
// ---------------------------------------------------------------------------
#if !defined(CATCH2_TEST_COMPILATION)
int main(int argc, char** argv)
{
g_gitHashBytes = new uint8_t[4U];
::memset(g_gitHashBytes, 0x00U, 4U);
uint32_t hash = ::strtoul(__GIT_VER_HASH__, 0, 16);
__SET_UINT32(hash, g_gitHashBytes, 0U);
if (argv[0] != nullptr && *argv[0] != 0)
g_progExe = std::string(argv[0]);
if (argc > 1) {
// check arguments
int i = checkArgs(argc, argv);
if (i < argc) {
argc -= i;
argv += i;
}
else {
argc--;
argv++;
}
}
::signal(SIGINT, sigHandler);
::signal(SIGTERM, sigHandler);
::signal(SIGHUP, sigHandler);
int ret = 0;
do {
g_signal = 0;
g_killed = false;
Dfsi *dfsi = new Dfsi(g_iniFile);
ret = dfsi->run();
delete dfsi;
if (g_signal == 2)
::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGINT");
if (g_signal == 15)
::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGTERM");
if (g_signal == 1)
::LogInfoEx(LOG_HOST, "[RSTR] dvmdfsi:main SIGHUP");
} while (g_signal == 1);
::LogFinalise();
::ActivityLogFinalise();
return ret;
}
#endif

@ -1,60 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @file DfsiMain.h
* @ingroup dfsi
* @file DfsiMain.cpp
* @ingroup dfsi
*/
#if !defined(__DFSI_MAIN_H__)
#define __DFSI_MAIN_H__
#include "Defines.h"
#include <string>
// ---------------------------------------------------------------------------
// Externs
// ---------------------------------------------------------------------------
/** @brief */
extern int g_signal;
/** @brief */
extern std::string g_progExe;
/** @brief */
extern std::string g_iniFile;
/** @brief */
extern std::string g_lockFile;
/** @brief (Global) Flag indicating foreground operation. */
extern bool g_foreground;
/** @brief (Global) Flag indicating the FNE should stop immediately. */
extern bool g_killed;
/** @brief */
extern std::string g_masterAddress;
/** @brief */
extern uint16_t g_masterPort;
/** @brief */
extern uint32_t g_peerId;
extern uint8_t* g_gitHashBytes;
/**
* @brief Helper to trigger a fatal error message. This will cause the program to terminate
* immediately with an error message.
*
* @param msg String format.
*
* This is a variable argument function.
*/
extern HOST_SW_API void fatal(const char* msg, ...);
#endif // __DFSI_MAIN_H__

@ -1,99 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
#include "CallData.h"
using namespace network;
using namespace modem;
using namespace p25;
using namespace p25::defines;
using namespace p25::dfsi::frames;
using namespace dfsi;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the VoiceCallData class. */
VoiceCallData::VoiceCallData() :
srcId(0U),
dstId(0U),
lco(0U),
mfId(MFG_STANDARD),
serviceOptions(0U),
lsd1(0U),
lsd2(0U),
mi(),
algoId(ALGO_UNENCRYPT),
kId(0U),
VHDR1(),
VHDR2(),
netLDU1(),
netLDU2(),
seqNo(0U),
n(0U),
streamId(0U)
{
mi = new uint8_t[MI_LENGTH_BYTES];
VHDR1 = new uint8_t[MotVoiceHeader1::HCW_LENGTH];
VHDR2 = new uint8_t[MotVoiceHeader2::HCW_LENGTH];
netLDU1 = new uint8_t[9U * 25U];
netLDU2 = new uint8_t[9U * 25U];
::memset(netLDU1, 0x00U, 9U * 25U);
::memset(netLDU2, 0x00U, 9U * 25U);
}
/* Finalizes a instance of the VoiceCallData class. */
VoiceCallData::~VoiceCallData()
{
delete[] mi;
delete[] VHDR1;
delete[] VHDR2;
delete[] netLDU1;
delete[] netLDU2;
}
/* Reset call data to defaults. */
void VoiceCallData::resetCallData()
{
srcId = 0U;
dstId = 0U;
lco = 0U;
mfId = MFG_STANDARD;
serviceOptions = 0U;
lsd1 = 0U;
lsd2 = 0U;
::memset(mi, 0x00U, MI_LENGTH_BYTES);
algoId = ALGO_UNENCRYPT;
kId = 0U;
::memset(VHDR1, 0x00U, MotVoiceHeader1::HCW_LENGTH);
::memset(VHDR2, 0x00U, MotVoiceHeader2::HCW_LENGTH);
::memset(netLDU1, 0x00U, 9U * 25U);
::memset(netLDU2, 0x00U, 9U * 25U);
n = 0U;
seqNo = 0U;
streamId = 0U;
}
/* Generate a new stream ID for a call. */
void VoiceCallData::newStreamId()
{
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
streamId = dist(random);
}

@ -1,153 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @file CallData.h
* @ingroup dfsi_network
* @file CallData.cpp
* @ingroup dfsi_network
*/
#if !defined(__DFSI_CALL_DATA_H__)
#define __DFSI_CALL_DATA_H__
#include "Defines.h"
#include "common/edac/RS634717.h"
#include "common/network/RawFrameQueue.h"
#include "common/p25/data/LowSpeedData.h"
#include "common/p25/P25Defines.h"
#include "common/p25/dfsi/DFSIDefines.h"
#include "common/p25/dfsi/LC.h"
#include "common/p25/dfsi/frames/Frames.h"
#include "common/Log.h"
#include "common/Utils.h"
#include "common/yaml/Yaml.h"
#include "common/RingBuffer.h"
#include "network/DfsiPeerNetwork.h"
#include "host/modem/Modem.h"
#include "host/modem/port/IModemPort.h"
#include "host/modem/port/UARTPort.h"
#include <random>
namespace network
{
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Represents an on-going call.
* @ingroup dfsi_network
*/
class HOST_SW_API VoiceCallData {
public:
/**
* @brief Initializes a new instance of the VoiceCallData class.
*/
VoiceCallData();
/**
* @brief Initializes a new instance of the VoiceCallData class.
*/
~VoiceCallData();
/**
* @brief Reset call data to defaults.
*/
void resetCallData();
/**
* @brief Generate a new stream ID for a call.
*/
void newStreamId();
/** @name Call Data */
/**
* @brief Source Radio ID.
*/
uint32_t srcId;
/**
* @brief Destination ID.
*/
uint32_t dstId;
/**
* @brief Link Control Opcode.
*/
uint8_t lco;
/**
* @brief Manufacturer ID.
*/
uint8_t mfId;
/**
* @brief Call Service Options.
*/
uint8_t serviceOptions;
/**
* @brief Low Speed Data 1.
*/
uint8_t lsd1;
/**
* @brief Low Speed Data 2.
*/
uint8_t lsd2;
/**
* @brief Encryption Message Indicator.
*/
uint8_t* mi;
/**
* @brief Encryption Algorithm ID.
*/
uint8_t algoId;
/**
* @brief Encryption Key ID.
*/
uint32_t kId;
/**
* @brief Voice Header 1.
*/
uint8_t* VHDR1;
/**
* @brief Voice Header 2.
*/
uint8_t* VHDR2;
/**
* @brief FNE Network LDU1 Buffer.
*/
uint8_t* netLDU1;
/**
* @brief FNE Network LDU2 Buffer.
*/
uint8_t* netLDU2;
/**
* @brief Sequence Number.
*/
uint32_t seqNo;
/**
* @brief
*/
uint8_t n;
/**
* @brief Stream ID.
*/
uint32_t streamId;
/** @} */
private:
// Used for stream ID generation
std::mt19937 random;
};
}
#endif // __DFSI_CALL_DATA_H__

@ -1,296 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "dfsi/Defines.h"
#include "common/network/json/json.h"
#include "common/p25/dfsi/DFSIDefines.h"
#include "common/p25/dfsi/LC.h"
#include "common/Utils.h"
#include "network/DfsiPeerNetwork.h"
using namespace network;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/* Initializes a new instance of the PeerNetwork class. */
DfsiPeerNetwork::DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password,
bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) :
Network(address, port, localPort, peerId, password, duplex, debug, dmr, p25, nxdn, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup)
{
assert(!address.empty());
assert(port > 0U);
assert(!password.empty());
}
/* Writes P25 LDU1 frame data to the network. */
bool DfsiPeerNetwork::writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, P25DEF::FrameType::E frameType)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
bool resetSeq = false;
if (m_p25StreamId == 0U) {
resetSeq = true;
m_p25StreamId = createStreamId();
}
uint32_t messageLength = 0U;
UInt8Array message = createP25_LDU1Message_Raw(messageLength, control, lsd, data, frameType);
if (message == nullptr) {
return false;
}
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
}
/* Writes P25 LDU2 frame data to the network. */
bool DfsiPeerNetwork::writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
bool resetSeq = false;
if (m_p25StreamId == 0U) {
resetSeq = true;
m_p25StreamId = createStreamId();
}
uint32_t messageLength = 0U;
UInt8Array message = createP25_LDU2Message_Raw(messageLength, control, lsd, data);
if (message == nullptr) {
return false;
}
return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId);
}
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/* Writes configuration to the network. */
bool DfsiPeerNetwork::writeConfig()
{
if (m_loginStreamId == 0U) {
LogWarning(LOG_NET, "BUGBUG: tried to write network authorisation with no stream ID?");
return false;
}
const char* software = __NETVER__;
json::object config = json::object();
// identity and frequency
config["identity"].set<std::string>(m_identity); // Identity
config["rxFrequency"].set<uint32_t>(m_rxFrequency); // Rx Frequency
config["txFrequency"].set<uint32_t>(m_txFrequency); // Tx Frequency
// system info
json::object sysInfo = json::object();
sysInfo["latitude"].set<float>(m_latitude); // Latitude
sysInfo["longitude"].set<float>(m_longitude); // Longitude
sysInfo["height"].set<int>(m_height); // Height
sysInfo["location"].set<std::string>(m_location); // Location
config["info"].set<json::object>(sysInfo);
// channel data
json::object channel = json::object();
channel["txPower"].set<uint32_t>(m_power); // Tx Power
channel["txOffsetMhz"].set<float>(m_txOffsetMhz); // Tx Offset (Mhz)
channel["chBandwidthKhz"].set<float>(m_chBandwidthKhz); // Ch. Bandwidth (khz)
channel["channelId"].set<uint8_t>(m_channelId); // Channel ID
channel["channelNo"].set<uint32_t>(m_channelNo); // Channel No
config["channel"].set<json::object>(channel);
// RCON
json::object rcon = json::object();
rcon["password"].set<std::string>(m_restApiPassword); // REST API Password
rcon["port"].set<uint16_t>(m_restApiPort); // REST API Port
config["rcon"].set<json::object>(rcon);
config["software"].set<std::string>(std::string(software)); // Software ID
json::value v = json::value(config);
std::string json = v.serialize();
char buffer[json.length() + 8U];
::memcpy(buffer + 0U, TAG_REPEATER_CONFIG, 4U);
::sprintf(buffer + 8U, "%s", json.c_str());
if (m_debug) {
Utils::dump(1U, "Network Message, Configuration", (uint8_t*)buffer, json.length() + 8U);
}
return writeMaster({ NET_FUNC::RPTC, NET_SUBFUNC::NOP }, (uint8_t*)buffer, json.length() + 8U, pktSeq(), m_loginStreamId);
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/* Creates an P25 LDU1 frame message. */
UInt8Array DfsiPeerNetwork::createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
const uint8_t* data, P25DEF::FrameType::E frameType)
{
using namespace p25::dfsi::defines;
using namespace p25::defines;
assert(data != nullptr);
p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd);
uint8_t* buffer = new uint8_t[P25_LDU1_PACKET_LENGTH + PACKET_PAD];
::memset(buffer, 0x00U, P25_LDU1_PACKET_LENGTH + PACKET_PAD);
// construct P25 message header
createP25_MessageHdr(buffer, DUID::LDU1, control, lsd, frameType);
// pack DFSI data
uint32_t count = MSG_HDR_SIZE;
uint8_t imbe[RAW_IMBE_LENGTH_BYTES];
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE1);
::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 24U, imbe);
count += DFSI_LDU1_VOICE1_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE2);
::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 46U, imbe);
count += DFSI_LDU1_VOICE2_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE3);
::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 60U, imbe);
count += DFSI_LDU1_VOICE3_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE4);
::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 77U, imbe);
count += DFSI_LDU1_VOICE4_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE5);
::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 94U, imbe);
count += DFSI_LDU1_VOICE5_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE6);
::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 111U, imbe);
count += DFSI_LDU1_VOICE6_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE7);
::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 128U, imbe);
count += DFSI_LDU1_VOICE7_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE8);
::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 145U, imbe);
count += DFSI_LDU1_VOICE8_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE9);
::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU1(buffer + 162U, imbe);
count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES;
buffer[23U] = count;
if (m_debug)
Utils::dump(1U, "Network Message, P25 LDU1", buffer, (P25_LDU1_PACKET_LENGTH + PACKET_PAD));
length = (P25_LDU1_PACKET_LENGTH + PACKET_PAD);
return UInt8Array(buffer);
}
/* Creates an P25 LDU2 frame message. */
UInt8Array DfsiPeerNetwork::createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
const uint8_t* data)
{
using namespace p25::dfsi::defines;
using namespace p25::defines;
assert(data != nullptr);
p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd);
uint8_t* buffer = new uint8_t[P25_LDU2_PACKET_LENGTH + PACKET_PAD];
::memset(buffer, 0x00U, P25_LDU2_PACKET_LENGTH + PACKET_PAD);
// construct P25 message header
createP25_MessageHdr(buffer, DUID::LDU2, control, lsd, FrameType::DATA_UNIT);
// pack DFSI data
uint32_t count = MSG_HDR_SIZE;
uint8_t imbe[RAW_IMBE_LENGTH_BYTES];
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE10);
::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 24U, imbe);
count += DFSI_LDU2_VOICE10_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE11);
::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 46U, imbe);
count += DFSI_LDU2_VOICE11_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE12);
::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 60U, imbe);
count += DFSI_LDU2_VOICE12_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE13);
::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 77U, imbe);
count += DFSI_LDU2_VOICE13_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE14);
::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 94U, imbe);
count += DFSI_LDU2_VOICE14_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE15);
::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 111U, imbe);
count += DFSI_LDU2_VOICE15_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE16);
::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 128U, imbe);
count += DFSI_LDU2_VOICE16_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE17);
::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 145U, imbe);
count += DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES;
dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE18);
::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES);
dfsiLC.encodeLDU2(buffer + 162U, imbe);
count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES;
buffer[23U] = count;
if (m_debug)
Utils::dump(1U, "Network Message, P25 LDU2", buffer, (P25_LDU2_PACKET_LENGTH + PACKET_PAD));
length = (P25_LDU2_PACKET_LENGTH + PACKET_PAD);
return UInt8Array(buffer);
}

@ -1,118 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
/**
* @file DfsiPeerNetwork.h
* @ingroup dfsi_network
* @file DfsiPeerNetwork.cpp
* @ingroup dfsi_network
*/
#if !defined(__DFSI_PEER_NETWORK_H__)
#define __DFSI_PEER_NETWORK_H__
#include "Defines.h"
#include "host/network/Network.h"
#include <string>
#include <cstdint>
namespace network
{
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Implements the core peer networking logic.
* @ingroup dfsi_network
*/
class HOST_SW_API DfsiPeerNetwork : public Network {
public:
/**
* @brief Initializes a new instance of the PeerNetwork class.
* @param address Network Hostname/IP address to connect to.
* @param port Network port number.
* @param local
* @param peerId Unique ID on the network.
* @param password Network authentication password.
* @param duplex Flag indicating full-duplex operation.
* @param debug Flag indicating whether network debug is enabled.
* @param dmr Flag indicating whether DMR is enabled.
* @param p25 Flag indicating whether P25 is enabled.
* @param nxdn Flag indicating whether NXDN is enabled.
* @param slot1 Flag indicating whether DMR slot 1 is enabled for network traffic.
* @param slot2 Flag indicating whether DMR slot 2 is enabled for network traffic.
* @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network.
* @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network.
* @param updateLookup Flag indicating that the system will accept radio ID and talkgroup ID lookups from the network.
*/
DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password,
bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup);
/**
* @brief Writes P25 LDU1 frame data to the network.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
* @param[in] data Buffer containing P25 LDU1 data to send.
* @param[in] frameType DVM P25 frame type.
* @returns bool True, if message was sent, otherwise false.
*/
bool writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data,
P25DEF::FrameType::E frameType) override;
/**
* @brief Writes P25 LDU2 frame data to the network.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
* @param[in] data Buffer containing P25 LDU2 data to send.
* @returns bool True, if message was sent, otherwise false.
*/
bool writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data) override;
protected:
/**
* @brief Writes configuration to the network.
* @returns bool True, if configuration was sent, otherwise false.
*/
bool writeConfig() override;
private:
/**
* @brief Creates an P25 LDU1 frame message.
*
* The data packed into a P25 LDU1 frame message is near standard DFSI messaging, just instead of
* 9 individual frames, they are packed into a single message one right after another.
*
* @param[out] length Length of network message buffer.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
* @param[in] data Buffer containing P25 LDU1 data to send.
* @param[in] frameType DVM P25 frame type.
* @returns UInt8Array Buffer containing the built network message.
*/
UInt8Array createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
const uint8_t* data, P25DEF::FrameType::E frameType);
/**
* @brief Creates an P25 LDU2 frame message.
*
* The data packed into a P25 LDU2 frame message is near standard DFSI messaging, just instead of
* 9 individual frames, they are packed into a single message one right after another.
*
* @param[out] length Length of network message buffer.
* @param[in] control Instance of p25::lc::LC containing link control data.
* @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data.
* @param[in] data Buffer containing P25 LDU2 data to send.
* @returns UInt8Array Buffer containing the built network message.
*/
UInt8Array createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
const uint8_t* data);
};
} // namespace network
#endif // __DFSI_PEER_NETWORK_H__

File diff suppressed because it is too large Load Diff

@ -1,264 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - DFSI V.24/UDP Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024 Patrick McDonnell, W3AXL
*
*/
/**
* @defgroup dfsi_network DFSI Networking/Serial Communications
* @brief Implementation for the DFSI networking and serial communications.
* @ingroup dfsi
*
* @file SerialService.h
* @ingroup dfsi_network
* @file SerialService.cpp
* @ingroup dfsi_network
*/
#if !defined(__SERIAL_SERVICE_H__)
#define __SERIAL_SERVICE_H__
#include "Defines.h"
#include "common/edac/RS634717.h"
#include "common/network/RawFrameQueue.h"
#include "common/p25/data/LowSpeedData.h"
#include "common/p25/P25Defines.h"
#include "common/p25/dfsi/DFSIDefines.h"
#include "common/p25/dfsi/LC.h"
#include "common/p25/dfsi/frames/Frames.h"
#include "common/Log.h"
#include "common/Utils.h"
#include "common/yaml/Yaml.h"
#include "common/RingBuffer.h"
#include "network/DfsiPeerNetwork.h"
#include "network/CallData.h"
#include "host/modem/Modem.h"
#include "host/modem/port/IModemPort.h"
#include "host/modem/port/UARTPort.h"
#include "host/modem/port/ModemNullPort.h"
#include <string>
#include <cstdint>
#include <map>
#include <unordered_map>
#include <random>
#include <pthread.h>
using namespace modem;
using namespace p25;
using namespace dfsi;
namespace network
{
/**
* @brief DFSI serial tx flags used to determine proper jitter handling of data in ringbuffer.
* @ingroup dfsi_network
*/
enum SERIAL_TX_TYPE {
NONIMBE,
IMBE
};
// ---------------------------------------------------------------------------
// Class Declaration
// ---------------------------------------------------------------------------
/**
* @brief Implements the serial V.24 communications service.
* @ingroup dfsi_network
*/
class HOST_SW_API SerialService {
public:
/**
* @brief Initializes an instance of the SerialService class.
* @param portType Serial port type.
* @param portName Serial port device name.
* @param baudrate Baud rate.
* @param rtrt Flag indicating whether or not RT/RT is enabled.
* @param diu Flag indicating whether or not V.24 communications are to a DIU.
* @param jitter
* @param network Instance of the DfsiPeerNetwork class.
* @param p25TxQueueSize Ringbuffer size for the P25 transmit queue.
* @param p25RxQueueSize Ringbuffer size for the P24 receive queue.
* @param callTimeout
* @param debug Flag indicating whether verbose debug logging is enabled.
* @param trace Flag indicating whether verbose trace logging is enabled.
*/
SerialService(std::string& portType, const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, uint16_t callTimeout, bool debug, bool trace);
/**
* @brief Finalizes an instance of the SerialService class.
*/
~SerialService();
/**
* @brief Updates the serial interface by the passed number of milliseconds.
* @param ms Number of milliseconds.
*/
void clock(uint32_t ms);
/**
* @brief Opens connection to the serial interface.
* @returns bool True, if serial interface has started, otherwise false.
*/
bool open();
/**
* @brief Closes connection to the serial interface.
*/
void close();
/**
* @brief Process data frames from the network.
* @param p25Buffer Buffer containing the network frame.
* @param length Length of the buffer.
*/
void processP25FromNet(UInt8Array p25Buffer, uint32_t length);
/**
* @brief Process data frames from the V.24 serial interface.
*
* This function pieces together LDU1/LDU2 messages from individual DFSI frames received over the serial port. It's
* called multiple times before an LDU is sent, and each time adds more data pieces to the LDUs.
*/
void processP25ToNet();
/**
* @brief Send the CMD_GET_VERSION to the connected V24 board to get firmware/UDID info
*/
void getBoardInfo();
private:
std::string m_portName;
uint32_t m_baudrate;
bool m_rtrt;
bool m_diu;
port::IModemPort* m_port;
uint16_t m_jitter;
bool m_debug;
bool m_trace;
DfsiPeerNetwork* m_network;
uint8_t* m_lastIMBE;
// Tables to keep track of calls
std::unordered_map<uint32_t, uint64_t> m_lastHeard;
std::unordered_map<uint32_t, uint32_t> m_sequences;
uint8_t* m_msgBuffer;
RESP_STATE m_msgState;
uint16_t m_msgLength;
uint16_t m_msgOffset;
DVM_COMMANDS m_msgType;
bool m_msgDoubleLength;
uint32_t m_netFrames;
uint32_t m_netLost;
RingBuffer<uint8_t> m_rxP25Queue;
RingBuffer<uint8_t> m_txP25Queue;
// Storage for V24 TX jitter buffer metering
uint64_t m_lastP25Tx;
edac::RS634717 m_rs;
// Counter for assembling a full LDU from individual frames in the RX queue
uint8_t m_rxP25LDUCounter;
// "Mutex" flags to indicate if calls to/from the FNE are already in progress
bool m_netCallInProgress;
bool m_lclCallInProgress;
// Time in ms to wait before considering a call in progress as "over" in case we miss the TDUs
uint16_t m_callTimeout;
// Storage for handling local/net call timeouts (for callInProgress mutexes)
uint64_t m_lastNetFrame;
uint64_t m_lastLclFrame;
// Control and LSD objects for current RX P25 data being built to send to the net
lc::LC* m_rxVoiceControl;
data::LowSpeedData* m_rxVoiceLsd;
// Call Data object for current RX P25 call (VHDR, LDU, etc)
VoiceCallData* m_rxVoiceCallData;
// The last LDU1 LC sent to the net, used to keep track of current call src/dst IDs, etc
p25::lc::LC* m_rxLastLDU1;
// Functions called by clock() to read/write from/to the serial port
/**
* @brief Read a data message from the serial interface.
* @return RESP_TYPE_DVM
*/
RESP_TYPE_DVM readSerial();
/**
* @brief Helper to write data from the P25 Tx queue to the serial interface.
*
* Very similar to the readP25Frame function.
*
* Note: the length encoded at the start does not include the length, tag, or timestamp bytes.
*
* @return int Actual number of bytes written to the serial interface.
*/
int writeSerial();
/**
* @brief Gets a frame of P25 data from the RX queue.
* @param data Buffer containing P25 data.
* @return uint32_t Size of the P25 data buffer, including leading data tag.
*/
uint32_t readP25Frame(uint8_t* data);
/**
* @brief Process a P25 LDU and add to the TX queue, timed appropriately.
* @param duid DUID.
* @param lc Instance of the dfsi::LC class.
* @param ldu Buffer containing LDU data.
*/
void writeP25Frame(P25DEF::DUID::E duid, dfsi::LC& lc, uint8_t* ldu);
/**
* @brief Send a start of stream sequence (HDU, etc) to the connected serial V24 device.
* @param lc Instance of the p25::LC class.
*/
void startOfStream(const LC& lc);
/**
* @brief Send an end of stream sequence (TDU, etc) to the connected serial V24 device.
*/
void endOfStream();
/**
* @brief Helper to add a V.24 dataframe to the P25 Tx queue with the proper timestamp and formatting.
* @param data Buffer containing V.24 data frame to send.
* @param len Length of buffer.
* @param msgType Type of message to send (used for proper jitter clocking).
*/
void addTxToQueue(uint8_t* data, uint16_t length, SERIAL_TX_TYPE msgType);
/**
* @brief Helper to insert IMBE silence frames for missing audio.
* @param data Buffer containing frame data.
* @param[out] lost Count of lost IMBE frames.
*/
void insertMissingAudio(uint8_t* data, uint32_t& lost);
/**
* @brief Resets the current rx call data (stream ID, TGIDs, etc)
*/
void rxResetCallData();
/**
* @brief
* @param buffer
* @param length
*/
void printDebug(const uint8_t* buffer, uint16_t length);
};
} // namespace network
#endif // __SERIAL_SERVICE_H__
Loading…
Cancel
Save

Powered by TurnKey Linux.