diff --git a/AX25Control.cpp b/AX25Control.cpp new file mode 100644 index 0000000..1f1b590 --- /dev/null +++ b/AX25Control.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 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; version 2 of the License. + * + * 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. + */ + +#include "AX25Control.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include +#include + +// #define DUMP_AX25 + +const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +CAX25Control::CAX25Control(CAX25Network* network) : +m_network(network), +m_enabled(true), +m_fp(NULL) +{ +} + +CAX25Control::~CAX25Control() +{ +} + +bool CAX25Control::writeModem(unsigned char *data, unsigned int len) +{ + assert(data != NULL); + + if (!m_enabled) + return false; + + unsigned char type = data[0U]; + + return true; +} + +bool CAX25Control::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "AX25_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("AX25", 1U, 4U, m_fp); + + return true; +} + +bool CAX25Control::writeFile(const unsigned char* data, unsigned int length) +{ + if (m_fp == NULL) + return false; + + ::fwrite(&length, 1U, sizeof(unsigned int), m_fp); + ::fwrite(data, 1U, length, m_fp); + + return true; +} + +void CAX25Control::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} + +void CAX25Control::enable(bool enabled) +{ + m_enabled = enabled; +} diff --git a/AX25Control.h b/AX25Control.h new file mode 100644 index 0000000..2f73874 --- /dev/null +++ b/AX25Control.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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(AX25Control_H) +#define AX25Control_H + +#include "AX25Network.h" + +#include + +class CAX25Control { +public: + CAX25Control(CAX25Network* network); + ~CAX25Control(); + + bool writeModem(unsigned char* data, unsigned int len); + + void enable(bool enabled); + +private: + CAX25Network* m_network; + bool m_enabled; + FILE* m_fp; + + bool openFile(); + bool writeFile(const unsigned char* data, unsigned int length); + void closeFile(); +}; + +#endif diff --git a/AX25Network.cpp b/AX25Network.cpp new file mode 100644 index 0000000..8123c51 --- /dev/null +++ b/AX25Network.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 "AX25Network.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CAX25Network::CAX25Network(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : +m_socket(localAddress, localPort), +m_address(), +m_port(gatewayPort), +m_debug(debug), +m_enabled(false) +{ + assert(gatewayPort > 0U); + assert(!gatewayAddress.empty()); + + m_address = CUDPSocket::lookup(gatewayAddress); +} + +CAX25Network::~CAX25Network() +{ +} + +bool CAX25Network::open() +{ + LogMessage("Opening AX25 network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +bool CAX25Network::writeAX25(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + unsigned char buffer[110U]; + ::memset(buffer, 0x00U, 110U); + + buffer[0U] = 'A'; + buffer[1U] = 'X'; + buffer[2U] = '2'; + buffer[3U] = '5'; + + ::memcpy(buffer + 4U, data, length); + + if (m_debug) + CUtils::dump(1U, "AX25 Network Data Sent", buffer, length + 4U); + + return m_socket.write(buffer, length + 4U, m_address, m_port); +} + +bool CAX25Network::writeMICE(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + unsigned char buffer[110U]; + ::memset(buffer, 0x00U, 110U); + + buffer[0U] = 'M'; + buffer[1U] = 'I'; + buffer[2U] = 'C'; + buffer[3U] = 'E'; + + ::memcpy(buffer + 4U, data, length); + + if (m_debug) + CUtils::dump(1U, "AX25 Network Data Sent", buffer, length + 4U); + + return m_socket.write(buffer, length + 4U, m_address, m_port); +} + +void CAX25Network::reset() +{ +} + +void CAX25Network::close() +{ + m_socket.close(); + + LogMessage("Closing AX25 network connection"); +} + +void CAX25Network::enable(bool enabled) +{ + m_enabled = enabled; +} diff --git a/AX25Network.h b/AX25Network.h new file mode 100644 index 0000000..b171e6a --- /dev/null +++ b/AX25Network.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 AX25Network_H +#define AX25Network_H + +#include "UDPSocket.h" + +#include +#include + +class CAX25Network { +public: + CAX25Network(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CAX25Network(); + + bool open(); + + void enable(bool enabled); + + bool writeAX25(const unsigned char* data, unsigned int length); + bool writeMICE(const unsigned char* data, unsigned int length); + + void reset(); + + void close(); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + bool m_debug; + bool m_enabled; +}; + +#endif diff --git a/Conf.cpp b/Conf.cpp index ee4034b..47fe532 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -45,12 +45,14 @@ enum SECTION { SECTION_NXDN, SECTION_POCSAG, SECTION_FM, + SECTION_AX25, SECTION_DSTAR_NETWORK, SECTION_DMR_NETWORK, SECTION_FUSION_NETWORK, SECTION_P25_NETWORK, SECTION_NXDN_NETWORK, SECTION_POCSAG_NETWORK, + SECTION_AX25_NETWORK, SECTION_TFTSERIAL, SECTION_HD44780, SECTION_NEXTION, @@ -205,6 +207,7 @@ m_fmCOSInvert(false), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtAudioBoost(1U), +m_ax25Enabled(false), m_dstarNetworkEnabled(false), m_dstarGatewayAddress(), m_dstarGatewayPort(0U), @@ -249,6 +252,12 @@ m_pocsagLocalAddress(), m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), +m_ax25NetworkEnabled(false), +m_ax25GatewayAddress(), +m_ax25GatewayPort(0U), +m_ax25LocalAddress(), +m_ax25LocalPort(0U), +m_ax25NetworkDebug(false), m_tftSerialPort("/dev/ttyAMA0"), m_tftSerialBrightness(50U), m_hd44780Rows(2U), @@ -342,6 +351,8 @@ bool CConf::read() section = SECTION_POCSAG; else if (::strncmp(buffer, "[FM]", 4U) == 0) section = SECTION_FM; + else if (::strncmp(buffer, "[AX.25]", 7U) == 0) + section = SECTION_AX25; else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) section = SECTION_DSTAR_NETWORK; else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) @@ -354,6 +365,8 @@ bool CConf::read() section = SECTION_NXDN_NETWORK; else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) section = SECTION_POCSAG_NETWORK; + else if (::strncmp(buffer, "[AX.25 Network]", 15U) == 0) + section = SECTION_AX25_NETWORK; else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) section = SECTION_TFTSERIAL; else if (::strncmp(buffer, "[HD44780]", 9U) == 0) @@ -700,8 +713,7 @@ bool CConf::read() m_pocsagEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Frequency") == 0) m_pocsagFrequency = (unsigned int)::atoi(value); - } - else if (section == SECTION_FM) { + } else if (section == SECTION_FM) { if (::strcmp(key, "Enable") == 0) m_fmEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Callsign") == 0) { @@ -775,6 +787,9 @@ bool CConf::read() m_fmMaxDevLevel = float(::atof(value)); else if (::strcmp(key, "ExtAudioBoost") == 0) m_fmExtAudioBoost = (unsigned int)::atoi(value); + } else if (section == SECTION_AX25) { + if (::strcmp(key, "Enable") == 0) + m_ax25Enabled = ::atoi(value) == 1; } else if (section == SECTION_DSTAR_NETWORK) { if (::strcmp(key, "Enable") == 0) m_dstarNetworkEnabled = ::atoi(value) == 1; @@ -869,6 +884,19 @@ bool CConf::read() m_pocsagNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_pocsagNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_AX25_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_ax25NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_ax25LocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_ax25LocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_ax25GatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_ax25GatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_ax25NetworkDebug = ::atoi(value) == 1; } else if (section == SECTION_TFTSERIAL) { if (::strcmp(key, "Port") == 0) m_tftSerialPort = value; @@ -1677,6 +1705,11 @@ unsigned int CConf::getFMExtAudioBoost() const return m_fmExtAudioBoost; } +bool CConf::getAX25Enabled() const +{ + return m_ax25Enabled; +} + bool CConf::getDStarNetworkEnabled() const { return m_dstarNetworkEnabled; @@ -1897,6 +1930,36 @@ bool CConf::getPOCSAGNetworkDebug() const return m_pocsagNetworkDebug; } +bool CConf::getAX25NetworkEnabled() const +{ + return m_ax25NetworkEnabled; +} + +std::string CConf::getAX25GatewayAddress() const +{ + return m_ax25GatewayAddress; +} + +unsigned int CConf::getAX25GatewayPort() const +{ + return m_ax25GatewayPort; +} + +std::string CConf::getAX25LocalAddress() const +{ + return m_ax25LocalAddress; +} + +unsigned int CConf::getAX25LocalPort() const +{ + return m_ax25LocalPort; +} + +bool CConf::getAX25NetworkDebug() const +{ + return m_ax25NetworkDebug; +} + std::string CConf::getTFTSerialPort() const { return m_tftSerialPort; diff --git a/Conf.h b/Conf.h index 40e81a0..91d7118 100644 --- a/Conf.h +++ b/Conf.h @@ -171,6 +171,9 @@ public: bool getPOCSAGEnabled() const; unsigned int getPOCSAGFrequency() const; + // The AX.25 section + bool getAX25Enabled() const; + // The FM Section bool getFMEnabled() const; std::string getFMCallsign() const; @@ -260,6 +263,14 @@ public: unsigned int getPOCSAGNetworkModeHang() const; bool getPOCSAGNetworkDebug() const; + // The AX.25 Network section + bool getAX25NetworkEnabled() const; + std::string getAX25GatewayAddress() const; + unsigned int getAX25GatewayPort() const; + std::string getAX25LocalAddress() const; + unsigned int getAX25LocalPort() const; + bool getAX25NetworkDebug() const; + // The TFTSERIAL section std::string getTFTSerialPort() const; unsigned int getTFTSerialBrightness() const; @@ -442,6 +453,8 @@ private: bool m_pocsagEnabled; unsigned int m_pocsagFrequency; + bool m_ax25Enabled; + bool m_fmEnabled; std::string m_fmCallsign; unsigned int m_fmCallsignSpeed; @@ -524,6 +537,13 @@ private: unsigned int m_pocsagNetworkModeHang; bool m_pocsagNetworkDebug; + bool m_ax25NetworkEnabled; + std::string m_ax25GatewayAddress; + unsigned int m_ax25GatewayPort; + std::string m_ax25LocalAddress; + unsigned int m_ax25LocalPort; + bool m_ax25NetworkDebug; + std::string m_tftSerialPort; unsigned int m_tftSerialBrightness; diff --git a/MMDVM.ini b/MMDVM.ini index 12f2b73..6f2b910 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -175,6 +175,9 @@ RFAudioBoost=1 MaxDevLevel=90 ExtAudioBoost=1 +[AX.25] +Enable=1 + [D-Star Network] Enable=1 GatewayAddress=127.0.0.1 @@ -231,6 +234,14 @@ GatewayPort=4800 # ModeHang=3 Debug=0 +[AX.25 Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=47325 +GatewayAddress=127.0.0.1 +GatewayPort=47326 +Debug=0 + [TFT Serial] # Port=modem Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 924d4a9..b1f8353 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -119,12 +119,14 @@ m_ysf(NULL), m_p25(NULL), m_nxdn(NULL), m_pocsag(NULL), +m_ax25(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), m_pocsagNetwork(NULL), +m_ax25Network(NULL), m_display(NULL), m_ump(NULL), m_mode(MODE_IDLE), @@ -151,6 +153,7 @@ m_p25Enabled(false), m_nxdnEnabled(false), m_pocsagEnabled(false), m_fmEnabled(false), +m_ax25Enabled(false), m_cwIdTime(0U), m_dmrLookup(NULL), m_nxdnLookup(NULL), @@ -320,6 +323,12 @@ int CMMDVMHost::run() return 1; } + if (m_ax25Enabled && m_conf.getAX25NetworkEnabled()) { + ret = createAX25Network(); + if (!ret) + return 1; + } + in_addr transparentAddress; unsigned int transparentPort = 0U; CUDPSocket* transparentSocket = NULL; @@ -614,6 +623,9 @@ int CMMDVMHost::run() pocsagTimer.start(); } + if (m_ax25Enabled) + m_ax25 = new CAX25Control(m_ax25Network); + bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { unsigned int port = m_conf.getRemoteControlPort(); @@ -806,6 +818,15 @@ int CMMDVMHost::run() } } + len = m_modem->readAX25Data(data); + if (m_ax25 != NULL && len > 0U) { + if (m_mode == MODE_IDLE || m_mode == MODE_FM) { + m_ax25->writeModem(data, len); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("NXDN modem data received when in mode %u", m_mode); + } + } + len = m_modem->readTransparentData(data); if (transparentSocket != NULL && len > 0U) transparentSocket->write(data, len, transparentAddress, transparentPort); @@ -1126,6 +1147,11 @@ int CMMDVMHost::run() delete m_pocsagNetwork; } + if (m_ax25Network != NULL) { + m_ax25Network->close(); + delete m_ax25Network; + } + if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; @@ -1142,6 +1168,7 @@ int CMMDVMHost::run() delete m_p25; delete m_nxdn; delete m_pocsag; + delete m_ax25; return 0; } @@ -1209,7 +1236,7 @@ bool CMMDVMHost::createModem() m_modem = CModem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug); m_modem->setSerialParams(protocol,address); - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel); m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency); m_modem->setDMRParams(colorCode); @@ -1531,6 +1558,34 @@ bool CMMDVMHost::createPOCSAGNetwork() return true; } +bool CMMDVMHost::createAX25Network() +{ + std::string gatewayAddress = m_conf.getAX25GatewayAddress(); + unsigned int gatewayPort = m_conf.getAX25GatewayPort(); + std::string localAddress = m_conf.getAX25LocalAddress(); + unsigned int localPort = m_conf.getAX25LocalPort(); + bool debug = m_conf.getAX25NetworkDebug(); + + LogInfo("AX.25 Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Address: %s", localAddress.c_str()); + LogInfo(" Local Port: %u", localPort); + + m_ax25Network = new CAX25Network(localAddress, localPort, gatewayAddress, gatewayPort, debug); + + bool ret = m_ax25Network->open(); + if (!ret) { + delete m_ax25Network; + m_ax25Network = NULL; + return false; + } + + m_ax25Network->enable(true); + + return true; +} + void CMMDVMHost::readParams() { m_dstarEnabled = m_conf.getDStarEnabled(); @@ -1540,6 +1595,7 @@ void CMMDVMHost::readParams() m_nxdnEnabled = m_conf.getNXDNEnabled(); m_pocsagEnabled = m_conf.getPOCSAGEnabled(); m_fmEnabled = m_conf.getFMEnabled(); + m_ax25Enabled = m_conf.getAX25Enabled(); m_duplex = m_conf.getDuplex(); m_callsign = m_conf.getCallsign(); m_id = m_conf.getId(); @@ -1557,8 +1613,10 @@ void CMMDVMHost::readParams() LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled"); LogInfo(" FM: %s", m_fmEnabled ? "enabled" : "disabled"); + LogInfo(" AX.25: %s", m_ax25Enabled ? "enabled" : "disabled"); } +// XXX AX.25 enabled/disabled void CMMDVMHost::setMode(unsigned char mode) { assert(m_modem != NULL); @@ -2016,9 +2074,13 @@ void CMMDVMHost::remoteControl() processEnableCommand(m_nxdnEnabled, true); break; case RCD_ENABLE_FM: - if (m_fmEnabled==false) + if (!m_fmEnabled) processEnableCommand(m_fmEnabled, true); break; + case RCD_ENABLE_AX25: + if (!m_ax25Enabled) + processEnableCommand(m_ax25Enabled, true); + break; case RCD_DISABLE_DSTAR: if (m_dstar != NULL && m_dstarEnabled==true) processEnableCommand(m_dstarEnabled, false); @@ -2043,6 +2105,10 @@ void CMMDVMHost::remoteControl() if (m_fmEnabled == true) processEnableCommand(m_fmEnabled, false); break; + case RCD_DISABLE_AX25: + if (m_ax25Enabled == true) + processEnableCommand(m_ax25Enabled, false); + break; case RCD_PAGE: if (m_pocsag != NULL) { unsigned int ric = m_remoteControl->getArgUInt(0U); @@ -2092,8 +2158,10 @@ void CMMDVMHost::processModeCommand(unsigned char mode, unsigned int timeout) void CMMDVMHost::processEnableCommand(bool& mode, bool enabled) { LogDebug("Setting mode current=%s new=%s",mode ? "true" : "false",enabled ? "true" : "false"); - mode=enabled; - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); + + mode = enabled; + + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); if (!m_modem->writeConfig()) LogError("Cannot write Config to MMDVM"); } diff --git a/MMDVMHost.h b/MMDVMHost.h index 17d6786..1a3f791 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -23,8 +23,10 @@ #include "POCSAGNetwork.h" #include "POCSAGControl.h" #include "DStarNetwork.h" +#include "AX25Network.h" #include "NXDNNetwork.h" #include "DStarControl.h" +#include "AX25Control.h" #include "DMRControl.h" #include "YSFControl.h" #include "P25Control.h" @@ -62,12 +64,14 @@ private: CP25Control* m_p25; CNXDNControl* m_nxdn; CPOCSAGControl* m_pocsag; + CAX25Control* m_ax25; CDStarNetwork* m_dstarNetwork; CDMRNetwork* m_dmrNetwork; CYSFNetwork* m_ysfNetwork; CP25Network* m_p25Network; CNXDNNetwork* m_nxdnNetwork; CPOCSAGNetwork* m_pocsagNetwork; + CAX25Network* m_ax25Network; CDisplay* m_display; CUMP* m_ump; unsigned char m_mode; @@ -94,6 +98,7 @@ private: bool m_nxdnEnabled; bool m_pocsagEnabled; bool m_fmEnabled; + bool m_ax25Enabled; unsigned int m_cwIdTime; CDMRLookup* m_dmrLookup; CNXDNLookup* m_nxdnLookup; @@ -114,6 +119,7 @@ private: bool createP25Network(); bool createNXDNNetwork(); bool createPOCSAGNetwork(); + bool createAX25Network(); void remoteControl(); void processModeCommand(unsigned char mode, unsigned int timeout); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 812c7a4..63b8aa6 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -153,6 +153,8 @@ + + @@ -251,6 +253,8 @@ + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index fb8a362..b333302 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -299,6 +299,12 @@ Header Files + + Header Files + + + Header Files + @@ -562,5 +568,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index efdaba6..2ac2552 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread LIBS = -lpthread LDFLAGS = -g -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Pi b/Makefile.Pi index 640c07f..3a4c2c1 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -6,7 +6,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DRASPBERRY_PI -I/usr/local/include LIBS = -lwiringPi -lwiringPiDev -lpthread LDFLAGS = -g -L/usr/local/lib -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index 24c2907..16775bb 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -7,7 +7,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -DADAFRUIT_DISPLAY -I/usr/l LIBS = -lwiringPi -lwiringPiDev -lpthread LDFLAGS = -g -L/usr/local/lib -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index c2faad2..3d02d71 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -6,7 +6,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -I/usr/local/include LIBS = -lwiringPi -lwiringPiDev -lpthread LDFLAGS = -g -L/usr/local/lib -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index d2db40c..f4f2024 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -6,7 +6,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DOLED -I/usr/local/include LIBS = -lArduiPi_OLED -lwiringPi -lpthread LDFLAGS = -g -L/usr/local/lib -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 4c5d3a3..3a70077 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -7,7 +7,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -DPCF8574_DISPLAY -I/usr/lo LIBS = -lwiringPi -lwiringPiDev -lpthread LDFLAGS = -g -L/usr/local/lib -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o \ diff --git a/Makefile.Solaris b/Makefile.Solaris index c50d41e..d469fad 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -6,7 +6,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread LIBS = -lpthread -lsocket LDFLAGS = -g -OBJECTS = \ +OBJECTS = AX25Control.o AX25Network.o \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o MobileGPS.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ diff --git a/Modem.cpp b/Modem.cpp index 93319be..0f0b0c1 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -76,6 +76,8 @@ const unsigned char MMDVM_NXDN_LOST = 0x41U; const unsigned char MMDVM_POCSAG_DATA = 0x50U; +const unsigned char MMDVM_AX25_DATA = 0x55U; + const unsigned char MMDVM_FM_PARAMS1 = 0x60U; const unsigned char MMDVM_FM_PARAMS2 = 0x61U; const unsigned char MMDVM_FM_PARAMS3 = 0x62U; @@ -133,6 +135,7 @@ m_p25Enabled(false), m_nxdnEnabled(false), m_pocsagEnabled(false), m_fmEnabled(false), +m_ax25Enabled(false), m_rxDCOffset(0), m_txDCOffset(0), m_serial(NULL), @@ -152,6 +155,7 @@ m_txP25Data(1000U, "Modem TX P25"), m_rxNXDNData(1000U, "Modem RX NXDN"), m_txNXDNData(1000U, "Modem TX NXDN"), m_txPOCSAGData(1000U, "Modem TX POCSAG"), +m_rxAX25Data(1000U, "Modem RX AX.25"), m_rxTransparentData(1000U, "Modem RX Transparent"), m_txTransparentData(1000U, "Modem TX Transparent"), m_sendTransparentDataFrameType(0U), @@ -230,7 +234,7 @@ void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int tx m_pocsagFrequency = pocsagFrequency + txOffset; } -void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled) +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled) { m_dstarEnabled = dstarEnabled; m_dmrEnabled = dmrEnabled; @@ -239,6 +243,7 @@ void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, m_nxdnEnabled = nxdnEnabled; m_pocsagEnabled = pocsagEnabled; m_fmEnabled = fmEnabled; + m_ax25Enabled = ax25Enabled; } void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel, float fmTXLevel) @@ -585,6 +590,20 @@ void CModem::clock(unsigned int ms) } break; + case MMDVM_AX25_DATA: { + if (m_trace) + CUtils::dump(1U, "RX AX.25 Data", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxAX25Data.addData(&data, 1U); + + data = TAG_DATA; + m_rxAX25Data.addData(&data, 1U); + + m_rxAX25Data.addData(m_buffer + 3U, m_length - 3U); + } + break; + case MMDVM_GET_STATUS: { // if (m_trace) // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); @@ -941,6 +960,20 @@ unsigned int CModem::readNXDNData(unsigned char* data) return len; } +unsigned int CModem::readAX25Data(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxAX25Data.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxAX25Data.getData(&len, 1U); + m_rxAX25Data.getData(data, len); + + return len; +} + unsigned int CModem::readTransparentData(unsigned char* data) { assert(data != NULL); @@ -1552,6 +1585,8 @@ bool CModem::setConfig() buffer[4U] |= 0x20U; if (m_fmEnabled && m_duplex) buffer[4U] |= 0x40U; + if (m_ax25Enabled) + buffer[4U] |= 0x80U; buffer[5U] = m_txDelay / 10U; // In 10ms units diff --git a/Modem.h b/Modem.h index 069fe46..69228bb 100644 --- a/Modem.h +++ b/Modem.h @@ -39,7 +39,7 @@ public: virtual void setSerialParams(const std::string& protocol, unsigned int address); virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency); - virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled); + virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled); virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel); virtual void setDMRParams(unsigned int colorCode); virtual void setYSFParams(bool loDev, unsigned int txHang); @@ -59,6 +59,7 @@ public: virtual unsigned int readYSFData(unsigned char* data); virtual unsigned int readP25Data(unsigned char* data); virtual unsigned int readNXDNData(unsigned char* data); + virtual unsigned int readAX25Data(unsigned char* data); virtual unsigned int readTransparentData(unsigned char* data); virtual unsigned int readSerial(unsigned char* data, unsigned int length); @@ -150,6 +151,7 @@ private: bool m_nxdnEnabled; bool m_pocsagEnabled; bool m_fmEnabled; + bool m_ax25Enabled; int m_rxDCOffset; int m_txDCOffset; CSerialController* m_serial; @@ -169,6 +171,7 @@ private: CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; CRingBuffer m_txPOCSAGData; + CRingBuffer m_rxAX25Data; CRingBuffer m_rxTransparentData; CRingBuffer m_txTransparentData; unsigned int m_sendTransparentDataFrameType; diff --git a/RemoteControl.cpp b/RemoteControl.cpp index 15ac46c..bf400ad 100644 --- a/RemoteControl.cpp +++ b/RemoteControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Jonathan Naylor G4KLX + * Copyright (C) 2019,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -102,6 +102,8 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_ENABLE_NXDN; else if (m_args.at(1U) == "fm") m_command = RCD_ENABLE_FM; + else if (m_args.at(1U) == "ax25") + m_command = RCD_ENABLE_AX25; } else if (m_args.at(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { if (m_args.at(1U) == "dstar") m_command = RCD_DISABLE_DSTAR; @@ -115,6 +117,8 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_DISABLE_NXDN; else if (m_args.at(1U) == "fm") m_command = RCD_DISABLE_FM; + else if (m_args.at(1U) == "ax25") + m_command = RCD_DISABLE_AX25; } else if (m_args.at(0U) == "page" && m_args.size() >= PAGE_ARGS) { // Page command is in the form of "page " m_command = RCD_PAGE; diff --git a/RemoteControl.h b/RemoteControl.h index 53b9820..3825dee 100644 --- a/RemoteControl.h +++ b/RemoteControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Jonathan Naylor G4KLX + * Copyright (C) 2019,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,12 +40,14 @@ enum REMOTE_COMMAND { RCD_ENABLE_P25, RCD_ENABLE_NXDN, RCD_ENABLE_FM, + RCD_ENABLE_AX25, RCD_DISABLE_DSTAR, RCD_DISABLE_DMR, RCD_DISABLE_YSF, RCD_DISABLE_P25, RCD_DISABLE_NXDN, RCD_DISABLE_FM, + RCD_DISABLE_AX25, RCD_PAGE, RCD_CW };