Add all site files

main
Ryan Collier 2 weeks ago
parent 8e6f03b5a6
commit 6a4e9bf610

@ -0,0 +1,119 @@
#!/bin/bash
# D-Star Site Install Script
# For Raspberry Pi 3 running Raspberry Pi OS Lite
# Usage: sudo bash install.sh
set -e
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MMDVM_SOURCE_DIR="/home/pi/MMDVM-Host"
MMDVM_REPO="https://github.com/g4klx/MMDVMHost.git"
ZEROTIER_NETWORK="c5029fdad4ccf9c5"
echo "================================================"
echo " D-Star Site Installer"
echo "================================================"
echo ""
# Select site
echo "Available sites:"
echo " 1) ORNG - Orange Portable"
echo " 2) ORNG2 - Orange TXRX"
echo ""
read -p "Select site [1-2]: " SITE_CHOICE
case $SITE_CHOICE in
1) SITE="ORNG" ;;
2) SITE="ORNG2" ;;
*) echo "Invalid selection"; exit 1 ;;
esac
echo ""
echo "Installing site: $SITE"
echo ""
# Load site config
source "$REPO_DIR/sites/$SITE/site.conf"
# Install dependencies
echo "Installing dependencies..."
apt-get update -qq
apt-get install -y git build-essential nlohmann-json3-dev libssl-dev python3
# Install ZeroTier
echo "Installing ZeroTier..."
curl -s https://install.zerotier.com | bash
zerotier-cli join $ZEROTIER_NETWORK
echo "ZeroTier installed and joined network $ZEROTIER_NETWORK"
echo "NOTE: Approve this device in ZeroTier Central before continuing"
echo ""
read -p "Press ENTER once device is approved in ZeroTier Central..."
# Enable serial port
echo "Configuring serial port..."
raspi-config nonint do_serial_hw 0
raspi-config nonint do_serial_cons 1
# Add pi user to dialout
usermod -a -G dialout pi
# Clone MMDVMHost if not present
if [ ! -d "$MMDVM_SOURCE_DIR" ]; then
echo "Cloning MMDVMHost..."
git clone "$MMDVM_REPO" "$MMDVM_SOURCE_DIR"
fi
# Copy patched source files
echo "Applying patches..."
cp "$REPO_DIR/src/DStarNetwork.cpp" "$MMDVM_SOURCE_DIR/"
cp "$REPO_DIR/src/DStarNetwork.h" "$MMDVM_SOURCE_DIR/"
cp "$REPO_DIR/src/Conf.h" "$MMDVM_SOURCE_DIR/"
cd "$MMDVM_SOURCE_DIR"
python3 "$REPO_DIR/src/patch_conf_cpp.py"
python3 "$REPO_DIR/src/patch_mmdvm_host_cpp.py"
# Compile
echo "Compiling MMDVMHost..."
make -j$(nproc)
# Install binary
echo "Installing binary..."
cp MMDVM-Host /usr/local/bin/MMDVM-Host
chmod 755 /usr/local/bin/MMDVM-Host
# Generate config from template
echo "Generating config for $SITE..."
sed \
-e "s/%%CALLSIGN%%/$CALLSIGN/g" \
-e "s/%%MODULE%%/$MODULE/g" \
-e "s/%%SITE_NAME%%/$SITE_NAME/g" \
-e "s/%%GATEWAY_ADDRESS%%/$GATEWAY_ADDRESS/g" \
-e "s/%%GATEWAY_PORT%%/$GATEWAY_PORT/g" \
-e "s/%%LOCAL_PORT%%/$LOCAL_PORT/g" \
-e "s/%%MODEM_PORT%%/$MODEM_PORT/g" \
-e "s/%%MODEM_SPEED%%/$MODEM_SPEED/g" \
"$REPO_DIR/sites/mmdvmhost.template" > /etc/mmdvmhost
echo "Config written to /etc/mmdvmhost"
# Install service
echo "Installing systemd service..."
cp "$REPO_DIR/services/mmdvmhost.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable mmdvmhost
systemctl start mmdvmhost
echo ""
echo "================================================"
echo " Installation complete for site: $SITE"
echo "================================================"
echo ""
echo "ZeroTier status:"
zerotier-cli status
echo ""
echo "Service status:"
systemctl status mmdvmhost --no-pager
echo ""
echo "Logs:"
echo " journalctl -u mmdvmhost -f"

@ -0,0 +1,14 @@
[Unit]
Description=MMDVMHost D-Star Site Service
After=network.target
After=time-sync.target
[Service]
Type=forking
ExecStart=/usr/local/bin/MMDVM-Host /etc/mmdvmhost
Restart=on-failure
RestartSec=10
User=root
[Install]
WantedBy=multi-user.target

@ -0,0 +1,9 @@
# ORANGE-PORTABLE Site Configuration
SITE_NAME=ORNG
CALLSIGN=N8MFN
MODULE=B
GATEWAY_ADDRESS=10.121.15.78
GATEWAY_PORT=20013
LOCAL_PORT=20016
MODEM_PORT=/dev/ttyAMA0
MODEM_SPEED=115200

@ -0,0 +1,9 @@
# ORANGE-TXRX Site Configuration
SITE_NAME=ORNG2
CALLSIGN=N8MFN
MODULE=B
GATEWAY_ADDRESS=10.121.15.78
GATEWAY_PORT=20013
LOCAL_PORT=20017
MODEM_PORT=/dev/ttyAMA0
MODEM_SPEED=115200

@ -0,0 +1,109 @@
[General]
Callsign=%%CALLSIGN%%
Id=0
Timeout=180
Duplex=1
ModeHang=20
Daemon=1
[Info]
RXFrequency=444000000
TXFrequency=449000000
Power=1
Latitude=0.0
Longitude=0.0
Height=0
Location=
Description=
URL=
[Log]
MQTTLevel=0
DisplayLevel=2
[MQTT]
Host=10.0.2.147
Port=1883
Keepalive=60
Name=%%SITE_NAME%%
Auth=0
[CW Id]
Enable=0
Time=10
Callsign=%%CALLSIGN%%
[Modem]
Protocol=uart
UARTPort=%%MODEM_PORT%%
UARTSpeed=%%MODEM_SPEED%%
RXInvert=0
TXInvert=0
PTTInvert=0
TXDelay=100
RXOffset=0
TXOffset=0
RXDCOffset=0
TXDCOffset=0
RFLevel=100
RXLevel=50
TXLevel=50
CWIdTXLevel=50
D-StarTXLevel=50
DMRTXLevel=50
YSFTXLevel=50
P25TXLevel=50
NXDNTXLevel=50
POCSAGTXLevel=50
FMTXLevel=50
UseCOSAsLockout=0
Trace=0
Debug=0
[D-Star]
Enable=1
Module=%%MODULE%%
SelfOnly=0
AckReply=1
AckTime=750
AckMessage=0
ErrorReply=1
RemoteGateway=0
ModeHang=20
[DMR]
Enable=0
[System Fusion]
Enable=0
[P25]
Enable=0
[NXDN]
Enable=0
[POCSAG]
Enable=0
[FM]
Enable=0
[D-Star Network]
Enable=1
GatewayAddress=%%GATEWAY_ADDRESS%%
GatewayPort=%%GATEWAY_PORT%%
LocalAddress=0.0.0.0
LocalPort=%%LOCAL_PORT%%
Name=%%SITE_NAME%%
ModeHang=20
Debug=0
[DMR Network]
Enable=0
[Lock File]
Enable=0
[Remote Control]
Enable=0

@ -0,0 +1,649 @@
/*
* Copyright (C) 2015-2026 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(CONF_H)
#define CONF_H
#include <string>
#include <vector>
#include <cstdint>
class CConf
{
public:
CConf(const std::string& file);
~CConf();
bool read();
// The General section
std::string getCallsign() const;
unsigned int getId() const;
unsigned int getTimeout() const;
bool getDuplex() const;
bool getDaemon() const;
// The Info section
unsigned int getRXFrequency() const;
unsigned int getTXFrequency() const;
unsigned int getPower() const;
float getLatitude() const;
float getLongitude() const;
int getHeight() const;
std::string getLocation() const;
std::string getDescription() const;
std::string getURL() const;
// The Log section
unsigned int getLogMQTTLevel() const;
unsigned int getLogDisplayLevel() const;
// The MQTT section
std::string getMQTTHost() const;
unsigned short getMQTTPort() const;
unsigned int getMQTTKeepalive() const;
std::string getMQTTName() const;
bool getMQTTAuthEnabled() const;
std::string getMQTTUsername() const;
std::string getMQTTPassword() const;
// The CW ID section
bool getCWIdEnabled() const;
unsigned int getCWIdTime() const;
std::string getCWIdCallsign() const;
#if defined(USE_DMR) || defined(USE_P25)
// The DMR Id section
std::string getDMRIdLookupFile() const;
unsigned int getDMRIdLookupTime() const;
#endif
#if defined(USE_NXDN)
// The NXDN Id section
std::string getNXDNIdLookupFile() const;
unsigned int getNXDNIdLookupTime() const;
#endif
// The Modem section
std::string getModemProtocol() const;
std::string getModemUARTPort() const;
unsigned int getModemUARTSpeed() const;
std::string getModemI2CPort() const;
unsigned int getModemI2CAddress() const;
std::string getModemModemAddress() const;
unsigned short getModemModemPort() const;
std::string getModemLocalAddress() const;
unsigned short getModemLocalPort() const;
bool getModemRXInvert() const;
bool getModemTXInvert() const;
bool getModemPTTInvert() const;
unsigned int getModemTXDelay() const;
#if defined(USE_DMR)
unsigned int getModemDMRDelay() const;
#endif
int getModemTXOffset() const;
int getModemRXOffset() const;
int getModemRXDCOffset() const;
int getModemTXDCOffset() const;
float getModemRFLevel() const;
float getModemRXLevel() const;
float getModemCWIdTXLevel() const;
#if defined(USE_DSTAR)
float getModemDStarTXLevel() const;
#endif
#if defined(USE_DMR)
float getModemDMRTXLevel() const;
#endif
#if defined(USE_YSF)
float getModemYSFTXLevel() const;
#endif
#if defined(USE_P25)
float getModemP25TXLevel() const;
#endif
#if defined(USE_NXDN)
float getModemNXDNTXLevel() const;
#endif
#if defined(USE_POCSAG)
float getModemPOCSAGTXLevel() const;
#endif
#if defined(USE_FM)
float getModemFMTXLevel() const;
#endif
std::string getModemRSSIMappingFile() const;
bool getModemUseCOSAsLockout() const;
bool getModemTrace() const;
bool getModemDebug() const;
// The Transparent Data section
bool getTransparentEnabled() const;
std::string getTransparentRemoteAddress() const;
unsigned short getTransparentRemotePort() const;
unsigned short getTransparentLocalPort() const;
unsigned int getTransparentSendFrameType() const;
#if defined(USE_DSTAR)
// The D-Star section
bool getDStarEnabled() const;
std::string getDStarModule() const;
bool getDStarSelfOnly() const;
std::vector<std::string> getDStarBlackList() const;
std::vector<std::string> getDStarWhiteList() const;
bool getDStarAckReply() const;
unsigned int getDStarAckTime() const;
DSTAR_ACK getDStarAckMessage() const;
bool getDStarErrorReply() const;
bool getDStarRemoteGateway() const;
unsigned int getDStarModeHang() const;
#endif
#if defined(USE_DMR)
// The DMR section
bool getDMREnabled() const;
DMR_BEACONS getDMRBeacons() const;
unsigned int getDMRBeaconInterval() const;
unsigned int getDMRBeaconDuration() const;
unsigned int getDMRId() const;
unsigned int getDMRColorCode() const;
bool getDMREmbeddedLCOnly() const;
bool getDMRDumpTAData() const;
bool getDMRSelfOnly() const;
std::vector<unsigned int> getDMRPrefixes() const;
std::vector<unsigned int> getDMRBlackList() const;
std::vector<unsigned int> getDMRWhiteList() const;
std::vector<unsigned int> getDMRSlot1TGWhiteList() const;
std::vector<unsigned int> getDMRSlot2TGWhiteList() const;
unsigned int getDMRCallHang() const;
unsigned int getDMRTXHang() const;
unsigned int getDMRModeHang() const;
DMR_OVCM getDMROVCM() const;
bool getDMRProtect() const;
#endif
#if defined(USE_YSF)
// The System Fusion section
bool getFusionEnabled() const;
bool getFusionLowDeviation() const;
bool getFusionRemoteGateway() const;
bool getFusionSelfOnly() const;
unsigned int getFusionTXHang() const;
unsigned int getFusionModeHang() const;
#endif
#if defined(USE_P25)
// The P25 section
bool getP25Enabled() const;
unsigned int getP25Id() const;
unsigned int getP25NAC() const;
bool getP25SelfOnly() const;
bool getP25OverrideUID() const;
bool getP25RemoteGateway() const;
unsigned int getP25TXHang() const;
unsigned int getP25ModeHang() const;
#endif
#if defined(USE_NXDN)
// The NXDN section
bool getNXDNEnabled() const;
unsigned int getNXDNId() const;
unsigned int getNXDNRAN() const;
bool getNXDNSelfOnly() const;
bool getNXDNRemoteGateway() const;
unsigned int getNXDNTXHang() const;
unsigned int getNXDNModeHang() const;
#endif
#if defined(USE_POCSAG)
// The POCSAG section
bool getPOCSAGEnabled() const;
unsigned int getPOCSAGFrequency() const;
#endif
#if defined(USE_FM)
// The FM Section
bool getFMEnabled() const;
std::string getFMCallsign() const;
unsigned int getFMCallsignSpeed() const;
unsigned int getFMCallsignFrequency() const;
unsigned int getFMCallsignTime() const;
unsigned int getFMCallsignHoldoff() const;
float getFMCallsignHighLevel() const;
float getFMCallsignLowLevel() const;
bool getFMCallsignAtStart() const;
bool getFMCallsignAtEnd() const;
bool getFMCallsignAtLatch() const;
std::string getFMRFAck() const;
std::string getFMExtAck() const;
unsigned int getFMAckSpeed() const;
unsigned int getFMAckFrequency() const;
unsigned int getFMAckMinTime() const;
unsigned int getFMAckDelay() const;
float getFMAckLevel() const;
unsigned int getFMTimeout() const;
float getFMTimeoutLevel() const;
float getFMCTCSSFrequency() const;
unsigned int getFMCTCSSHighThreshold() const;
unsigned int getFMCTCSSLowThreshold() const;
float getFMCTCSSLevel() const;
unsigned int getFMKerchunkTime() const;
unsigned int getFMHangTime() const;
unsigned int getFMAccessMode() const;
bool getFMLinkMode() const;
bool getFMCOSInvert() const;
bool getFMNoiseSquelch() const;
unsigned int getFMSquelchHighThreshold() const;
unsigned int getFMSquelchLowThreshold() const;
unsigned int getFMRFAudioBoost() const;
float getFMMaxDevLevel() const;
unsigned int getFMExtAudioBoost() const;
unsigned int getFMModeHang() const;
#endif
#if defined(USE_DSTAR)
// The D-Star Network section
bool getDStarNetworkEnabled() const;
std::string getDStarGatewayAddress() const;
unsigned short getDStarGatewayPort() const;
std::string getDStarLocalAddress() const;
unsigned short getDStarLocalPort() const;
std::string getDStarNetworkName() const;
unsigned int getDStarNetworkModeHang() const;
bool getDStarNetworkDebug() const;
#endif
#if defined(USE_DMR)
// The DMR Network section
bool getDMRNetworkEnabled() const;
std::string getDMRNetworkGatewayAddress() const;
unsigned short getDMRNetworkGatewayPort() const;
std::string getDMRNetworkLocalAddress() const;
unsigned short getDMRNetworkLocalPort() const;
bool getDMRNetworkDebug() const;
unsigned int getDMRNetworkJitter() const;
bool getDMRNetworkSlot1() const;
bool getDMRNetworkSlot2() const;
unsigned int getDMRNetworkModeHang() const;
#endif
#if defined(USE_YSF)
// The System Fusion Network section
bool getFusionNetworkEnabled() const;
std::string getFusionNetworkLocalAddress() const;
unsigned short getFusionNetworkLocalPort() const;
std::string getFusionNetworkGatewayAddress() const;
unsigned short getFusionNetworkGatewayPort() const;
unsigned int getFusionNetworkModeHang() const;
bool getFusionNetworkDebug() const;
#endif
#if defined(USE_P25)
// The P25 Network section
bool getP25NetworkEnabled() const;
std::string getP25GatewayAddress() const;
unsigned short getP25GatewayPort() const;
std::string getP25LocalAddress() const;
unsigned short getP25LocalPort() const;
unsigned int getP25NetworkModeHang() const;
bool getP25NetworkDebug() const;
#endif
#if defined(USE_NXDN)
// The NXDN Network section
bool getNXDNNetworkEnabled() const;
std::string getNXDNNetworkProtocol() const;
std::string getNXDNGatewayAddress() const;
unsigned short getNXDNGatewayPort() const;
std::string getNXDNLocalAddress() const;
unsigned short getNXDNLocalPort() const;
unsigned int getNXDNNetworkModeHang() const;
bool getNXDNNetworkDebug() const;
#endif
#if defined(USE_POCSAG)
// The POCSAG Network section
bool getPOCSAGNetworkEnabled() const;
std::string getPOCSAGGatewayAddress() const;
unsigned short getPOCSAGGatewayPort() const;
std::string getPOCSAGLocalAddress() const;
unsigned short getPOCSAGLocalPort() const;
unsigned int getPOCSAGNetworkModeHang() const;
bool getPOCSAGNetworkDebug() const;
#endif
#if defined(USE_FM)
// The FM Network section
bool getFMNetworkEnabled() const;
std::string getFMGatewayAddress() const;
unsigned short getFMGatewayPort() const;
std::string getFMLocalAddress() const;
unsigned short getFMLocalPort() const;
bool getFMPreEmphasis() const;
bool getFMDeEmphasis() const;
float getFMTXAudioGain() const;
float getFMRXAudioGain() const;
unsigned int getFMNetworkModeHang() const;
bool getFMNetworkDebug() const;
#endif
// The Lock File section
bool getLockFileEnabled() const;
std::string getLockFileName() const;
// The Remote Control section
bool getRemoteControlEnabled() const;
private:
std::string m_file;
std::string m_callsign;
unsigned int m_id;
unsigned int m_timeout;
bool m_duplex;
bool m_daemon;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
unsigned int m_power;
float m_latitude;
float m_longitude;
int m_height;
std::string m_location;
std::string m_description;
std::string m_url;
unsigned int m_logMQTTLevel;
unsigned int m_logDisplayLevel;
std::string m_mqttHost;
unsigned short m_mqttPort;
unsigned int m_mqttKeepalive;
std::string m_mqttName;
bool m_mqttAuthEnabled;
std::string m_mqttUsername;
std::string m_mqttPassword;
bool m_cwIdEnabled;
unsigned int m_cwIdTime;
std::string m_cwIdCallsign;
#if defined(USE_DMR) || defined(USE_P25)
std::string m_dmrIdLookupFile;
unsigned int m_dmrIdLookupTime;
#endif
#if defined(USE_NXDN)
std::string m_nxdnIdLookupFile;
unsigned int m_nxdnIdLookupTime;
#endif
std::string m_modemProtocol;
std::string m_modemUARTPort;
unsigned int m_modemUARTSpeed;
std::string m_modemI2CPort;
unsigned int m_modemI2CAddress;
std::string m_modemModemAddress;
unsigned short m_modemModemPort;
std::string m_modemLocalAddress;
unsigned short m_modemLocalPort;
bool m_modemRXInvert;
bool m_modemTXInvert;
bool m_modemPTTInvert;
unsigned int m_modemTXDelay;
#if defined(USE_DMR)
unsigned int m_modemDMRDelay;
#endif
int m_modemTXOffset;
int m_modemRXOffset;
int m_modemRXDCOffset;
int m_modemTXDCOffset;
float m_modemRFLevel;
float m_modemRXLevel;
float m_modemCWIdTXLevel;
float m_modemDStarTXLevel;
float m_modemDMRTXLevel;
float m_modemYSFTXLevel;
float m_modemP25TXLevel;
float m_modemNXDNTXLevel;
float m_modemPOCSAGTXLevel;
float m_modemFMTXLevel;
std::string m_modemRSSIMappingFile;
bool m_modemUseCOSAsLockout;
bool m_modemTrace;
bool m_modemDebug;
bool m_transparentEnabled;
std::string m_transparentRemoteAddress;
unsigned short m_transparentRemotePort;
unsigned short m_transparentLocalPort;
unsigned int m_transparentSendFrameType;
#if defined(USE_DSTAR)
bool m_dstarEnabled;
std::string m_dstarModule;
bool m_dstarSelfOnly;
std::vector<std::string> m_dstarBlackList;
std::vector<std::string> m_dstarWhiteList;
bool m_dstarAckReply;
unsigned int m_dstarAckTime;
DSTAR_ACK m_dstarAckMessage;
bool m_dstarErrorReply;
bool m_dstarRemoteGateway;
#endif
unsigned int m_dstarModeHang;
#if defined(USE_DMR)
bool m_dmrEnabled;
DMR_BEACONS m_dmrBeacons;
unsigned int m_dmrBeaconInterval;
unsigned int m_dmrBeaconDuration;
#endif
unsigned int m_dmrId;
#if defined(USE_DMR)
unsigned int m_dmrColorCode;
bool m_dmrSelfOnly;
bool m_dmrEmbeddedLCOnly;
bool m_dmrDumpTAData;
std::vector<unsigned int> m_dmrPrefixes;
std::vector<unsigned int> m_dmrBlackList;
std::vector<unsigned int> m_dmrWhiteList;
std::vector<unsigned int> m_dmrSlot1TGWhiteList;
std::vector<unsigned int> m_dmrSlot2TGWhiteList;
unsigned int m_dmrCallHang;
unsigned int m_dmrTXHang;
#endif
unsigned int m_dmrModeHang;
#if defined(USE_DMR)
DMR_OVCM m_dmrOVCM;
bool m_dmrProtect;
#endif
#if defined(USE_YSF)
bool m_fusionEnabled;
bool m_fusionLowDeviation;
bool m_fusionRemoteGateway;
bool m_fusionSelfOnly;
unsigned int m_fusionTXHang;
#endif
unsigned int m_fusionModeHang;
#if defined(USE_P25)
bool m_p25Enabled;
#endif
unsigned int m_p25Id;
#if defined(USE_P25)
unsigned int m_p25NAC;
bool m_p25SelfOnly;
bool m_p25OverrideUID;
bool m_p25RemoteGateway;
unsigned int m_p25TXHang;
#endif
unsigned int m_p25ModeHang;
#if defined(USE_NXDN)
bool m_nxdnEnabled;
unsigned int m_nxdnId;
unsigned int m_nxdnRAN;
bool m_nxdnSelfOnly;
bool m_nxdnRemoteGateway;
unsigned int m_nxdnTXHang;
#endif
unsigned int m_nxdnModeHang;
#if defined(USE_POCSAG)
bool m_pocsagEnabled;
#endif
unsigned int m_pocsagFrequency;
#if defined(USE_FM)
bool m_fmEnabled;
#endif
std::string m_fmCallsign;
#if defined(USE_FM)
unsigned int m_fmCallsignSpeed;
unsigned int m_fmCallsignFrequency;
unsigned int m_fmCallsignTime;
unsigned int m_fmCallsignHoldoff;
float m_fmCallsignHighLevel;
float m_fmCallsignLowLevel;
bool m_fmCallsignAtStart;
bool m_fmCallsignAtEnd;
bool m_fmCallsignAtLatch;
std::string m_fmRFAck;
std::string m_fmExtAck;
unsigned int m_fmAckSpeed;
unsigned int m_fmAckFrequency;
unsigned int m_fmAckMinTime;
unsigned int m_fmAckDelay;
float m_fmAckLevel;
#endif
unsigned int m_fmTimeout;
#if defined(USE_FM)
float m_fmTimeoutLevel;
float m_fmCTCSSFrequency;
unsigned int m_fmCTCSSHighThreshold;
unsigned int m_fmCTCSSLowThreshold;
float m_fmCTCSSLevel;
unsigned int m_fmKerchunkTime;
unsigned int m_fmHangTime;
unsigned int m_fmAccessMode;
bool m_fmLinkMode;
bool m_fmCOSInvert;
bool m_fmNoiseSquelch;
unsigned int m_fmSquelchHighThreshold;
unsigned int m_fmSquelchLowThreshold;
unsigned int m_fmRFAudioBoost;
float m_fmMaxDevLevel;
unsigned int m_fmExtAudioBoost;
#endif
unsigned int m_fmModeHang;
#if defined(USE_DSTAR)
bool m_dstarNetworkEnabled;
std::string m_dstarGatewayAddress;
unsigned short m_dstarGatewayPort;
std::string m_dstarLocalAddress;
unsigned short m_dstarLocalPort;
std::string m_dstarNetworkName;
#endif
unsigned int m_dstarNetworkModeHang;
#if defined(USE_DSTAR)
bool m_dstarNetworkDebug;
#endif
#if defined(USE_DMR)
bool m_dmrNetworkEnabled;
std::string m_dmrNetworkGatewayAddress;
unsigned short m_dmrNetworkGatewayPort;
std::string m_dmrNetworkLocalAddress;
unsigned short m_dmrNetworkLocalPort;
bool m_dmrNetworkDebug;
unsigned int m_dmrNetworkJitter;
bool m_dmrNetworkSlot1;
bool m_dmrNetworkSlot2;
#endif
unsigned int m_dmrNetworkModeHang;
#if defined(USE_YSF)
bool m_fusionNetworkEnabled;
std::string m_fusionNetworkLocalAddress;
unsigned short m_fusionNetworkLocalPort;
std::string m_fusionNetworkGatewayAddress;
unsigned short m_fusionNetworkGatewayPort;
#endif
unsigned int m_fusionNetworkModeHang;
#if defined(USE_YSF)
bool m_fusionNetworkDebug;
#endif
#if defined(USE_P25)
bool m_p25NetworkEnabled;
std::string m_p25GatewayAddress;
unsigned short m_p25GatewayPort;
std::string m_p25LocalAddress;
unsigned short m_p25LocalPort;
#endif
unsigned int m_p25NetworkModeHang;
#if defined(USE_P25)
bool m_p25NetworkDebug;
#endif
#if defined(USE_NXDN)
bool m_nxdnNetworkEnabled;
std::string m_nxdnNetworkProtocol;
std::string m_nxdnGatewayAddress;
unsigned short m_nxdnGatewayPort;
std::string m_nxdnLocalAddress;
unsigned short m_nxdnLocalPort;
#endif
unsigned int m_nxdnNetworkModeHang;
#if defined(USE_NXDN)
bool m_nxdnNetworkDebug;
#endif
#if defined(USE_POCSAG)
bool m_pocsagNetworkEnabled;
std::string m_pocsagGatewayAddress;
unsigned short m_pocsagGatewayPort;
std::string m_pocsagLocalAddress;
unsigned short m_pocsagLocalPort;
unsigned int m_pocsagNetworkModeHang;
bool m_pocsagNetworkDebug;
#endif
#if defined(USE_FM)
bool m_fmNetworkEnabled;
std::string m_fmGatewayAddress;
unsigned short m_fmGatewayPort;
std::string m_fmLocalAddress;
unsigned short m_fmLocalPort;
bool m_fmPreEmphasis;
bool m_fmDeEmphasis;
float m_fmTXAudioGain;
float m_fmRXAudioGain;
#endif
unsigned int m_fmNetworkModeHang;
#if defined(USE_FM)
bool m_fmNetworkDebug;
#endif
bool m_lockFileEnabled;
std::string m_lockFileName;
bool m_remoteControlEnabled;
};
#endif

@ -0,0 +1,392 @@
/*
* Copyright (C) 2009-2014,2016,2019,2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "DStarNetwork.h"
#include "StopWatch.h"
#include "Defines.h"
#include "Utils.h"
#include "Log.h"
#if defined(USE_DSTAR)
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
const unsigned int BUFFER_LENGTH = 100U;
CDStarNetwork::CDStarNetwork(const std::string& gatewayAddress, unsigned short gatewayPort, const std::string& localAddress, unsigned short localPort, bool duplex, const char* version, const std::string& name, bool debug) :
m_socket(localAddress, localPort),
m_addr(),
m_addrLen(0U),
m_duplex(duplex),
m_version(version),
m_name(name),
m_debug(debug),
m_enabled(false),
m_outId(0U),
m_outSeq(0U),
m_inId(0U),
m_buffer(1000U, "D-Star Network"),
m_pollTimer(1000U, 60U),
m_linkStatus(LINK_STATUS::NONE),
m_linkReflector(nullptr),
m_random()
{
if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
m_linkReflector = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
::memset(m_linkReflector, 0, DSTAR_LONG_CALLSIGN_LENGTH);
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
}
CDStarNetwork::~CDStarNetwork()
{
delete[] m_linkReflector;
}
bool CDStarNetwork::open()
{
if (m_addrLen == 0U) {
LogError("Unable to resolve the address of the ircDDB Gateway");
return false;
}
LogMessage("Opening D-Star network connection");
m_pollTimer.start();
bool ret = m_socket.open(m_addr);
if (!ret)
return false;
// Send registration with site name if configured
if (!m_name.empty()) {
LogMessage("D-Star registering with split controller as \"%s\"", m_name.c_str());
writeRegister();
}
return true;
}
bool CDStarNetwork::writeHeader(const unsigned char* header, unsigned int length, bool busy)
{
assert(header != nullptr);
unsigned char buffer[50U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = busy ? 0x22U : 0x20U;
// Create a random id for this transmission
std::uniform_int_distribution<uint16_t> dist(0x0001, 0xfffe);
m_outId = dist(m_random);
buffer[5] = m_outId / 256U; // Unique session id
buffer[6] = m_outId % 256U;
buffer[7] = 0U;
::memcpy(buffer + 8U, header, length);
m_outSeq = 0U;
if (m_debug)
CUtils::dump(1U, "D-Star Network Header Sent", buffer, 49U);
for (unsigned int i = 0U; i < 2U; i++) {
bool ret = m_socket.write(buffer, 49U, m_addr, m_addrLen);
if (!ret)
return false;
}
return true;
}
bool CDStarNetwork::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end, bool busy)
{
assert(data != nullptr);
unsigned char buffer[30U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = busy ? 0x23U : 0x21U;
buffer[5] = m_outId / 256U; // Unique session id
buffer[6] = m_outId % 256U;
// If this is a data sync, reset the sequence to zero
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
m_outSeq = 0U;
buffer[7] = m_outSeq;
if (end)
buffer[7] |= 0x40U; // End of data marker
buffer[8] = errors;
m_outSeq++;
if (m_outSeq > 0x14U)
m_outSeq = 0U;
::memcpy(buffer + 9U, data, length);
if (m_debug)
CUtils::dump(1U, "D-Star Network Data Sent", buffer, length + 9U);
return m_socket.write(buffer, length + 9U, m_addr, m_addrLen);
}
bool CDStarNetwork::writePoll(const char* text)
{
assert(text != nullptr);
unsigned char buffer[40U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = 0x0AU; // Poll with text
unsigned int length = (unsigned int)::strlen(text);
// Include the nul at the end also
::memcpy(buffer + 5U, text, length + 1U);
// if (m_debug)
// CUtils::dump(1U, "D-Star Network Poll Sent", buffer, 6U + length);
return m_socket.write(buffer, 6U + length, m_addr, m_addrLen);
}
bool CDStarNetwork::writeRegister()
{
unsigned char buffer[40U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = 0x0BU; // NETWORK_REGISTER with name
unsigned int length = (unsigned int)m_name.length();
if (length > 34U) length = 34U; // buffer[40] - offset[5] - null[1]
for (unsigned int i = 0U; i < length; i++)
buffer[5U + i] = m_name[i];
buffer[5U + length] = 0x00U;
if (m_debug)
CUtils::dump(1U, "D-Star Network Register Sent", buffer, 6U + length);
return m_socket.write(buffer, 6U + length, m_addr, m_addrLen);
}
void CDStarNetwork::clock(unsigned int ms)
{
m_pollTimer.clock(ms);
if (m_pollTimer.hasExpired()) {
char text[60U];
#if defined(_WIN32) || defined(_WIN64)
if (m_duplex)
::sprintf(text, "win_mmdvm-%s", m_version);
else
::sprintf(text, "win_mmdvm-dvmega-%s", m_version);
#else
if (m_duplex)
::sprintf(text, "linux_mmdvm-%s", m_version);
else
::sprintf(text, "linux_mmdvm-dvmega-%s", m_version);
#endif
writePoll(text);
// Send registration keepalive if name is configured
if (!m_name.empty())
writeRegister();
m_pollTimer.start();
}
unsigned char buffer[BUFFER_LENGTH];
sockaddr_storage address;
unsigned int addrLen;
int length = m_socket.read(buffer, BUFFER_LENGTH, address, addrLen);
if (length <= 0)
return;
if (!CUDPSocket::match(m_addr, address)) {
LogMessage("D-Star, packet received from an invalid source");
return;
}
// Invalid packet type?
if (::memcmp(buffer, "DSRP", 4U) != 0)
return;
switch (buffer[4]) {
case 0x00U: // NETWORK_TEXT;
if (m_debug)
CUtils::dump(1U, "D-Star Network Status Received", buffer, length);
m_linkStatus = LINK_STATUS(buffer[25U]);
::memcpy(m_linkReflector, buffer + 26U, DSTAR_LONG_CALLSIGN_LENGTH);
LogMessage("D-Star link status set to \"%20.20s\"", buffer + 5U);
return;
case 0x01U: // NETWORK_TEMPTEXT;
case 0x04U: // NETWORK_STATUS1..5
case 0x0AU: // POLL
case 0x24U: // NETWORK_DD_DATA
return;
case 0x20U: // NETWORK_HEADER
if (m_inId == 0U && m_enabled) {
if (m_debug)
CUtils::dump(1U, "D-Star Network Header Received", buffer, length);
m_inId = buffer[5] * 256U + buffer[6];
unsigned char c = length - 7U;
m_buffer.addData(&c, 1U);
c = TAG_HEADER;
m_buffer.addData(&c, 1U);
m_buffer.addData(buffer + 8U, length - 8U);
}
break;
case 0x21U: // NETWORK_DATA
if (m_enabled) {
if (m_debug)
CUtils::dump(1U, "D-Star Network Data Received", buffer, length);
uint16_t id = buffer[5] * 256U + buffer[6];
// Check that the stream id matches the valid header, reject otherwise
if (id == m_inId && m_enabled) {
unsigned char ctrl[3U];
ctrl[0U] = length - 7U;
// Is this the last packet in the stream?
if ((buffer[7] & 0x40) == 0x40) {
m_inId = 0U;
ctrl[1U] = TAG_EOT;
} else {
ctrl[1U] = TAG_DATA;
}
ctrl[2U] = buffer[7] & 0x3FU;
m_buffer.addData(ctrl, 3U);
m_buffer.addData(buffer + 9U, length - 9U);
}
}
break;
default:
CUtils::dump("Unknown D-Star packet from the Gateway", buffer, length);
break;
}
}
unsigned int CDStarNetwork::read(unsigned char* data, unsigned int length)
{
assert(data != nullptr);
if (m_buffer.isEmpty())
return 0U;
unsigned char c = 0U;
m_buffer.getData(&c, 1U);
assert(c <= 100U);
assert(c <= length);
unsigned char buffer[100U];
m_buffer.getData(buffer, c);
switch (buffer[0U]) {
case TAG_HEADER:
case TAG_DATA:
case TAG_EOT:
::memcpy(data, buffer, c);
return c;
default:
return 0U;
}
}
void CDStarNetwork::reset()
{
m_inId = 0U;
}
bool CDStarNetwork::isConnected() const
{
return (m_addrLen != 0);
}
void CDStarNetwork::close()
{
m_socket.close();
LogMessage("Closing D-Star network connection");
}
void CDStarNetwork::enable(bool enabled)
{
if (enabled && !m_enabled)
reset();
else if (!enabled && m_enabled)
m_buffer.clear();
m_enabled = enabled;
}
void CDStarNetwork::getStatus(LINK_STATUS& status, unsigned char* reflector)
{
assert(reflector != nullptr);
status = m_linkStatus;
::memcpy(reflector, m_linkReflector, DSTAR_LONG_CALLSIGN_LENGTH);
}
#endif

@ -0,0 +1,82 @@
/*
* Copyright (C) 2009-2014,2016,2020,2021,2023 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DStarNetwork_H
#define DStarNetwork_H
#include "DStarDefines.h"
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "Defines.h"
#include "Timer.h"
#if defined(USE_DSTAR)
#include <cstdint>
#include <string>
#include <random>
class CDStarNetwork {
public:
CDStarNetwork(const std::string& gatewayAddress, unsigned short gatewayPort, const std::string& localAddress, unsigned short localPort, bool duplex, const char* version, const std::string& name, bool debug);
~CDStarNetwork();
bool open();
void enable(bool enabled);
bool writeHeader(const unsigned char* header, unsigned int length, bool busy);
bool writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end, bool busy);
void getStatus(LINK_STATUS& status, unsigned char* reflector);
unsigned int read(unsigned char* data, unsigned int length);
void reset();
bool isConnected() const;
void close();
void clock(unsigned int ms);
private:
CUDPSocket m_socket;
sockaddr_storage m_addr;
unsigned int m_addrLen;
bool m_duplex;
const char* m_version;
std::string m_name;
bool m_debug;
bool m_enabled;
uint16_t m_outId;
uint8_t m_outSeq;
uint16_t m_inId;
CRingBuffer<unsigned char> m_buffer;
CTimer m_pollTimer;
LINK_STATUS m_linkStatus;
unsigned char* m_linkReflector;
std::mt19937 m_random;
bool writePoll(const char* text);
bool writeRegister();
};
#endif
#endif

@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Patches Conf.cpp to add DStarNetworkName support"""
import sys
with open('Conf.cpp', 'r') as f:
content = f.read()
# 1. Add initializer after m_dstarLocalPort(0U),
old = 'm_dstarLocalPort(0U),\n#endif\nm_dstarNetworkModeHang(3U),'
new = 'm_dstarLocalPort(0U),\nm_dstarNetworkName(),\n#endif\nm_dstarNetworkModeHang(3U),'
if old not in content:
print("ERROR: Could not find initializer insertion point")
sys.exit(1)
content = content.replace(old, new, 1)
print("1. Initializer added OK")
# 2. Add Name= parser - single tabs
old = '\t\t\telse if (::strcmp(key, "LocalPort") == 0)\n\t\t\t\tm_dstarLocalPort = (unsigned short)::atoi(value);\n\t\t\telse if (::strcmp(key, "ModeHang") == 0)\n\t\t\t\tm_dstarNetworkModeHang = (unsigned int)::atoi(value);\n\t\t\telse if (::strcmp(key, "Debug") == 0)\n\t\t\t\tm_dstarNetworkDebug = ::atoi(value) == 1;\n#endif\n#if defined(USE_DMR)\n\t\t} else if (section == SECTION::DMR_NETWORK) {'
new = '\t\t\telse if (::strcmp(key, "LocalPort") == 0)\n\t\t\t\tm_dstarLocalPort = (unsigned short)::atoi(value);\n\t\t\telse if (::strcmp(key, "Name") == 0)\n\t\t\t\tm_dstarNetworkName = value;\n\t\t\telse if (::strcmp(key, "ModeHang") == 0)\n\t\t\t\tm_dstarNetworkModeHang = (unsigned int)::atoi(value);\n\t\t\telse if (::strcmp(key, "Debug") == 0)\n\t\t\t\tm_dstarNetworkDebug = ::atoi(value) == 1;\n#endif\n#if defined(USE_DMR)\n\t\t} else if (section == SECTION::DMR_NETWORK) {'
if old not in content:
print("ERROR: Could not find Name= parser insertion point")
sys.exit(1)
content = content.replace(old, new, 1)
print("2. Name= parser added OK")
# 3. Add getDStarNetworkName() getter - try tabs first then spaces
old = 'unsigned int CConf::getDStarNetworkModeHang() const\n{\n\treturn m_dstarNetworkModeHang;\n}'
new = 'std::string CConf::getDStarNetworkName() const\n{\n\treturn m_dstarNetworkName;\n}\n\nunsigned int CConf::getDStarNetworkModeHang() const\n{\n\treturn m_dstarNetworkModeHang;\n}'
if old not in content:
old = 'unsigned int CConf::getDStarNetworkModeHang() const\n{\n return m_dstarNetworkModeHang;\n}'
new = 'std::string CConf::getDStarNetworkName() const\n{\n return m_dstarNetworkName;\n}\n\nunsigned int CConf::getDStarNetworkModeHang() const\n{\n return m_dstarNetworkModeHang;\n}'
if old not in content:
print("ERROR: Could not find getter insertion point")
sys.exit(1)
content = content.replace(old, new, 1)
print("3. getDStarNetworkName() getter added OK")
with open('Conf.cpp', 'w') as f:
f.write(content)
print("Conf.cpp patched successfully")

@ -0,0 +1,22 @@
#!/usr/bin/env python3
"""Patches MMDVM-Host.cpp to add DStarNetworkName support"""
import sys
with open('MMDVM-Host.cpp', 'r') as f:
content = f.read()
old = '\tbool debug = m_conf.getDStarNetworkDebug();\n\tm_dstarNetModeHang = m_conf.getDStarNetworkModeHang();\n\n\tLogInfo("D-Star Network Parameters");\n\tLogInfo(" Gateway Address: %s", gatewayAddress.c_str());\n\tLogInfo(" Gateway Port: %hu", gatewayPort);\n\tLogInfo(" Local Address: %s", localAddress.c_str());\n\tLogInfo(" Local Port: %hu", localPort);\n\tLogInfo(" Mode Hang: %us", m_dstarNetModeHang);\n\n\tm_dstarNetwork = new CDStarNetwork(gatewayAddress, gatewayPort, localAddress, localPort, m_duplex, VERSION, debug);'
new = '\tbool debug = m_conf.getDStarNetworkDebug();\n\tstd::string name = m_conf.getDStarNetworkName();\n\tm_dstarNetModeHang = m_conf.getDStarNetworkModeHang();\n\n\tLogInfo("D-Star Network Parameters");\n\tLogInfo(" Gateway Address: %s", gatewayAddress.c_str());\n\tLogInfo(" Gateway Port: %hu", gatewayPort);\n\tLogInfo(" Local Address: %s", localAddress.c_str());\n\tLogInfo(" Local Port: %hu", localPort);\n\tLogInfo(" Mode Hang: %us", m_dstarNetModeHang);\n\tif (!name.empty())\n\t\tLogInfo(" Name: %s", name.c_str());\n\n\tm_dstarNetwork = new CDStarNetwork(gatewayAddress, gatewayPort, localAddress, localPort, m_duplex, VERSION, name, debug);'
if old not in content:
print("ERROR: Could not find createDStarNetwork insertion point")
sys.exit(1)
content = content.replace(old, new, 1)
print("createDStarNetwork() patched OK")
with open('MMDVM-Host.cpp', 'w') as f:
f.write(content)
print("MMDVM-Host.cpp patched successfully")

@ -0,0 +1,49 @@
#!/bin/bash
# D-Star Site Update Script
# Pulls latest patches and rebuilds MMDVMHost
# Usage: sudo bash update.sh
set -e
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MMDVM_SOURCE_DIR="/home/pi/MMDVM-Host"
echo "================================================"
echo " D-Star Site Updater"
echo "================================================"
# Pull latest repo
echo "Pulling latest from Gitea..."
cd "$REPO_DIR"
git pull
# Pull latest MMDVMHost source
echo "Pulling latest MMDVMHost source..."
cd "$MMDVM_SOURCE_DIR"
git pull
# Restore original files before patching
echo "Restoring originals..."
git checkout DStarNetwork.cpp DStarNetwork.h Conf.h Conf.cpp MMDVM-Host.cpp
# Apply patches
echo "Applying patches..."
cp "$REPO_DIR/src/DStarNetwork.cpp" ./
cp "$REPO_DIR/src/DStarNetwork.h" ./
cp "$REPO_DIR/src/Conf.h" ./
python3 "$REPO_DIR/src/patch_conf_cpp.py"
python3 "$REPO_DIR/src/patch_mmdvm_host_cpp.py"
# Recompile
echo "Compiling..."
make -j$(nproc)
# Install
echo "Installing..."
systemctl stop mmdvmhost
cp MMDVM-Host /usr/local/bin/MMDVM-Host
systemctl start mmdvmhost
echo ""
echo "Update complete"
systemctl status mmdvmhost --no-pager
Loading…
Cancel
Save

Powered by TurnKey Linux.