Merge remote-tracking branch 'refs/remotes/g4klx/master'

This commit is contained in:
KD4Z 2018-08-22 10:17:13 -04:00
commit 000ef35c13
100 changed files with 2784 additions and 703 deletions

124
Conf.cpp
View file

@ -43,11 +43,13 @@ enum SECTION {
SECTION_FUSION,
SECTION_P25,
SECTION_NXDN,
SECTION_POCSAG,
SECTION_DSTAR_NETWORK,
SECTION_DMR_NETWORK,
SECTION_FUSION_NETWORK,
SECTION_P25_NETWORK,
SECTION_NXDN_NETWORK,
SECTION_POCSAG_NETWORK,
SECTION_TFTSERIAL,
SECTION_HD44780,
SECTION_NEXTION,
@ -85,6 +87,8 @@ m_dmrIdLookupTime(0U),
m_nxdnIdLookupFile(),
m_nxdnIdLookupTime(0U),
m_modemPort(),
m_modemProtocol("uart"),
m_modemAddress(0x22),
m_modemRXInvert(false),
m_modemTXInvert(false),
m_modemPTTInvert(false),
@ -102,6 +106,7 @@ m_modemDMRTXLevel(50.0F),
m_modemYSFTXLevel(50.0F),
m_modemP25TXLevel(50.0F),
m_modemNXDNTXLevel(50.0F),
m_modemPOCSAGTXLevel(50.0F),
m_modemRSSIMappingFile(),
m_modemTrace(false),
m_modemDebug(false),
@ -109,6 +114,7 @@ m_transparentEnabled(false),
m_transparentRemoteAddress(),
m_transparentRemotePort(0U),
m_transparentLocalPort(0U),
m_transparentSendFrameType(0U),
m_umpEnabled(false),
m_umpPort(),
m_dstarEnabled(false),
@ -141,6 +147,7 @@ m_fusionEnabled(false),
m_fusionLowDeviation(false),
m_fusionRemoteGateway(false),
m_fusionSelfOnly(false),
m_fusionTXHang(4U),
m_fusionSQLEnabled(false),
m_fusionSQL(0U),
m_fusionModeHang(10U),
@ -157,6 +164,8 @@ m_nxdnRAN(1U),
m_nxdnSelfOnly(false),
m_nxdnRemoteGateway(false),
m_nxdnModeHang(10U),
m_pocsagEnabled(false),
m_pocsagFrequency(0U),
m_dstarNetworkEnabled(false),
m_dstarGatewayAddress(),
m_dstarGatewayPort(0U),
@ -170,7 +179,7 @@ m_dmrNetworkLocal(0U),
m_dmrNetworkPassword(),
m_dmrNetworkOptions(),
m_dmrNetworkDebug(false),
m_dmrNetworkJitter(300U),
m_dmrNetworkJitter(360U),
m_dmrNetworkSlot1(true),
m_dmrNetworkSlot2(true),
m_dmrNetworkModeHang(3U),
@ -194,6 +203,13 @@ m_nxdnLocalAddress(),
m_nxdnLocalPort(0U),
m_nxdnNetworkModeHang(3U),
m_nxdnNetworkDebug(false),
m_pocsagNetworkEnabled(false),
m_pocsagGatewayAddress(),
m_pocsagGatewayPort(0U),
m_pocsagLocalAddress(),
m_pocsagLocalPort(0U),
m_pocsagNetworkModeHang(3U),
m_pocsagNetworkDebug(false),
m_tftSerialPort("/dev/ttyAMA0"),
m_tftSerialBrightness(50U),
m_hd44780Rows(2U),
@ -272,6 +288,8 @@ bool CConf::read()
section = SECTION_P25;
else if (::strncmp(buffer, "[NXDN]", 6U) == 0)
section = SECTION_NXDN;
else if (::strncmp(buffer, "[POCSAG]", 8U) == 0)
section = SECTION_POCSAG;
else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0)
section = SECTION_DSTAR_NETWORK;
else if (::strncmp(buffer, "[DMR Network]", 13U) == 0)
@ -282,6 +300,8 @@ bool CConf::read()
section = SECTION_P25_NETWORK;
else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0)
section = SECTION_NXDN_NETWORK;
else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0)
section = SECTION_POCSAG_NETWORK;
else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0)
section = SECTION_TFTSERIAL;
else if (::strncmp(buffer, "[HD44780]", 9U) == 0)
@ -338,7 +358,7 @@ bool CConf::read()
m_daemon = ::atoi(value) == 1;
} else if (section == SECTION_INFO) {
if (::strcmp(key, "TXFrequency") == 0)
m_txFrequency = (unsigned int)::atoi(value);
m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value);
else if (::strcmp(key, "RXFrequency") == 0)
m_rxFrequency = (unsigned int)::atoi(value);
else if (::strcmp(key, "Power") == 0)
@ -388,6 +408,10 @@ bool CConf::read()
} else if (section == SECTION_MODEM) {
if (::strcmp(key, "Port") == 0)
m_modemPort = value;
else if (::strcmp(key, "Protocol") == 0)
m_modemProtocol = value;
else if (::strcmp(key, "Address") == 0)
m_modemAddress = (unsigned int)::strtoul(value, NULL, 16);
else if (::strcmp(key, "RXInvert") == 0)
m_modemRXInvert = ::atoi(value) == 1;
else if (::strcmp(key, "TXInvert") == 0)
@ -424,6 +448,8 @@ bool CConf::read()
m_modemP25TXLevel = float(::atof(value));
else if (::strcmp(key, "NXDNTXLevel") == 0)
m_modemNXDNTXLevel = float(::atof(value));
else if (::strcmp(key, "POCSAGTXLevel") == 0)
m_modemPOCSAGTXLevel = float(::atof(value));
else if (::strcmp(key, "RSSIMappingFile") == 0)
m_modemRSSIMappingFile = value;
else if (::strcmp(key, "Trace") == 0)
@ -439,6 +465,8 @@ bool CConf::read()
m_transparentRemotePort = (unsigned int)::atoi(value);
else if (::strcmp(key, "LocalPort") == 0)
m_transparentLocalPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "SendFrameType") == 0)
m_transparentSendFrameType = (unsigned int)::atoi(value);
} else if (section == SECTION_UMP) {
if (::strcmp(key, "Enable") == 0)
m_umpEnabled = ::atoi(value) == 1;
@ -553,6 +581,8 @@ bool CConf::read()
m_fusionRemoteGateway = ::atoi(value) == 1;
else if (::strcmp(key, "SelfOnly") == 0)
m_fusionSelfOnly = ::atoi(value) == 1;
else if (::strcmp(key, "TXHang") == 0)
m_fusionTXHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "ModeHang") == 0)
m_fusionModeHang = (unsigned int)::atoi(value);
} else if (section == SECTION_P25) {
@ -583,6 +613,11 @@ bool CConf::read()
m_nxdnRemoteGateway = ::atoi(value) == 1;
else if (::strcmp(key, "ModeHang") == 0)
m_nxdnModeHang = (unsigned int)::atoi(value);
} else if (section == SECTION_POCSAG) {
if (::strcmp(key, "Enable") == 0)
m_pocsagEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Frequency") == 0)
m_pocsagFrequency = (unsigned int)::atoi(value);
} else if (section == SECTION_DSTAR_NETWORK) {
if (::strcmp(key, "Enable") == 0)
m_dstarNetworkEnabled = ::atoi(value) == 1;
@ -662,6 +697,21 @@ bool CConf::read()
m_nxdnNetworkModeHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_nxdnNetworkDebug = ::atoi(value) == 1;
} else if (section == SECTION_POCSAG_NETWORK) {
if (::strcmp(key, "Enable") == 0)
m_pocsagNetworkEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "LocalAddress") == 0)
m_pocsagLocalAddress = value;
else if (::strcmp(key, "LocalPort") == 0)
m_pocsagLocalPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "GatewayAddress") == 0)
m_pocsagGatewayAddress = value;
else if (::strcmp(key, "GatewayPort") == 0)
m_pocsagGatewayPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "ModeHang") == 0)
m_pocsagNetworkModeHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_pocsagNetworkDebug = ::atoi(value) == 1;
} else if (section == SECTION_TFTSERIAL) {
if (::strcmp(key, "Port") == 0)
m_tftSerialPort = value;
@ -872,6 +922,16 @@ std::string CConf::getModemPort() const
return m_modemPort;
}
std::string CConf::getModemProtocol() const
{
return m_modemProtocol;
}
unsigned int CConf::getModemAddress() const
{
return m_modemAddress;
}
bool CConf::getModemRXInvert() const
{
return m_modemRXInvert;
@ -957,6 +1017,11 @@ float CConf::getModemNXDNTXLevel() const
return m_modemNXDNTXLevel;
}
float CConf::getModemPOCSAGTXLevel() const
{
return m_modemPOCSAGTXLevel;
}
std::string CConf::getModemRSSIMappingFile () const
{
return m_modemRSSIMappingFile;
@ -992,6 +1057,11 @@ unsigned int CConf::getTransparentLocalPort() const
return m_transparentLocalPort;
}
unsigned int CConf::getTransparentSendFrameType() const
{
return m_transparentSendFrameType;
}
bool CConf::getUMPEnabled() const
{
return m_umpEnabled;
@ -1147,6 +1217,11 @@ bool CConf::getFusionRemoteGateway() const
return m_fusionRemoteGateway;
}
unsigned int CConf::getFusionTXHang() const
{
return m_fusionTXHang;
}
bool CConf::getFusionSelfOnly() const
{
return m_fusionSelfOnly;
@ -1232,6 +1307,16 @@ unsigned int CConf::getNXDNModeHang() const
return m_nxdnModeHang;
}
bool CConf::getPOCSAGEnabled() const
{
return m_pocsagEnabled;
}
unsigned int CConf::getPOCSAGFrequency() const
{
return m_pocsagFrequency;
}
bool CConf::getDStarNetworkEnabled() const
{
return m_dstarNetworkEnabled;
@ -1417,6 +1502,41 @@ bool CConf::getNXDNNetworkDebug() const
return m_nxdnNetworkDebug;
}
bool CConf::getPOCSAGNetworkEnabled() const
{
return m_pocsagNetworkEnabled;
}
std::string CConf::getPOCSAGGatewayAddress() const
{
return m_pocsagGatewayAddress;
}
unsigned int CConf::getPOCSAGGatewayPort() const
{
return m_pocsagGatewayPort;
}
std::string CConf::getPOCSAGLocalAddress() const
{
return m_pocsagLocalAddress;
}
unsigned int CConf::getPOCSAGLocalPort() const
{
return m_pocsagLocalPort;
}
unsigned int CConf::getPOCSAGNetworkModeHang() const
{
return m_pocsagNetworkModeHang;
}
bool CConf::getPOCSAGNetworkDebug() const
{
return m_pocsagNetworkDebug;
}
std::string CConf::getTFTSerialPort() const
{
return m_tftSerialPort;

34
Conf.h
View file

@ -70,6 +70,8 @@ public:
// The Modem section
std::string getModemPort() const;
std::string getModemProtocol() const;
unsigned int getModemAddress() const;
bool getModemRXInvert() const;
bool getModemTXInvert() const;
bool getModemPTTInvert() const;
@ -87,6 +89,7 @@ public:
float getModemYSFTXLevel() const;
float getModemP25TXLevel() const;
float getModemNXDNTXLevel() const;
float getModemPOCSAGTXLevel() const;
std::string getModemRSSIMappingFile() const;
bool getModemTrace() const;
bool getModemDebug() const;
@ -96,6 +99,7 @@ public:
std::string getTransparentRemoteAddress() const;
unsigned int getTransparentRemotePort() const;
unsigned int getTransparentLocalPort() const;
unsigned int getTransparentSendFrameType() const;
// The UMP section
bool getUMPEnabled() const;
@ -136,6 +140,7 @@ public:
bool getFusionLowDeviation() const;
bool getFusionRemoteGateway() const;
bool getFusionSelfOnly() const;
unsigned int getFusionTXHang() const;
bool getFusionSQLEnabled() const;
unsigned char getFusionSQL() const;
unsigned int getFusionModeHang() const;
@ -157,6 +162,10 @@ public:
bool getNXDNRemoteGateway() const;
unsigned int getNXDNModeHang() const;
// The POCSAG section
bool getPOCSAGEnabled() const;
unsigned int getPOCSAGFrequency() const;
// The D-Star Network section
bool getDStarNetworkEnabled() const;
std::string getDStarGatewayAddress() const;
@ -204,6 +213,15 @@ public:
unsigned int getNXDNNetworkModeHang() const;
bool getNXDNNetworkDebug() const;
// The POCSAG Network section
bool getPOCSAGNetworkEnabled() const;
std::string getPOCSAGGatewayAddress() const;
unsigned int getPOCSAGGatewayPort() const;
std::string getPOCSAGLocalAddress() const;
unsigned int getPOCSAGLocalPort() const;
unsigned int getPOCSAGNetworkModeHang() const;
bool getPOCSAGNetworkDebug() const;
// The TFTSERIAL section
std::string getTFTSerialPort() const;
unsigned int getTFTSerialBrightness() const;
@ -277,6 +295,8 @@ private:
unsigned int m_nxdnIdLookupTime;
std::string m_modemPort;
std::string m_modemProtocol;
unsigned int m_modemAddress;
bool m_modemRXInvert;
bool m_modemTXInvert;
bool m_modemPTTInvert;
@ -294,6 +314,7 @@ private:
float m_modemYSFTXLevel;
float m_modemP25TXLevel;
float m_modemNXDNTXLevel;
float m_modemPOCSAGTXLevel;
std::string m_modemRSSIMappingFile;
bool m_modemTrace;
bool m_modemDebug;
@ -302,6 +323,7 @@ private:
std::string m_transparentRemoteAddress;
unsigned int m_transparentRemotePort;
unsigned int m_transparentLocalPort;
unsigned int m_transparentSendFrameType;
bool m_umpEnabled;
std::string m_umpPort;
@ -338,6 +360,7 @@ private:
bool m_fusionLowDeviation;
bool m_fusionRemoteGateway;
bool m_fusionSelfOnly;
unsigned int m_fusionTXHang;
bool m_fusionSQLEnabled;
unsigned char m_fusionSQL;
unsigned int m_fusionModeHang;
@ -357,6 +380,9 @@ private:
bool m_nxdnRemoteGateway;
unsigned int m_nxdnModeHang;
bool m_pocsagEnabled;
unsigned int m_pocsagFrequency;
bool m_dstarNetworkEnabled;
std::string m_dstarGatewayAddress;
unsigned int m_dstarGatewayPort;
@ -399,6 +425,14 @@ private:
unsigned int m_nxdnNetworkModeHang;
bool m_nxdnNetworkDebug;
bool m_pocsagNetworkEnabled;
std::string m_pocsagGatewayAddress;
unsigned int m_pocsagGatewayPort;
std::string m_pocsagLocalAddress;
unsigned int m_pocsagLocalPort;
unsigned int m_pocsagNetworkModeHang;
bool m_pocsagNetworkDebug;
std::string m_tftSerialPort;
unsigned int m_tftSerialBrightness;

View file

@ -26,6 +26,7 @@
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
const unsigned int BUFFER_LENGTH = 500U;
@ -33,6 +34,7 @@ const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) :
m_addressStr(address),
m_address(),
m_port(port),
m_id(NULL),
@ -122,6 +124,9 @@ bool CDMRNetwork::open()
{
LogMessage("DMR, Opening DMR Network");
if (m_address.s_addr == INADDR_NONE)
m_address = CUDPSocket::lookup(m_addressStr);
m_status = WAITING_CONNECT;
m_timeoutTimer.stop();
m_retryTimer.start();
@ -544,6 +549,9 @@ bool CDMRNetwork::writeConfig()
case HWT_MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HWT_NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
default:
software = "MMDVM_Unknown";
break;
@ -570,6 +578,9 @@ bool CDMRNetwork::writeConfig()
case HWT_NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
case HWT_NANO_DV:
software = "MMDVM_Nano_DV";
break;
case HWT_MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;

View file

@ -57,6 +57,7 @@ public:
void close();
private:
std::string m_addressStr;
in_addr m_address;
unsigned int m_port;
uint8_t* m_id;

View file

@ -89,6 +89,7 @@ m_rfLC(NULL),
m_netLC(NULL),
m_rfSeqNo(0U),
m_rfN(0U),
m_lastrfN(0U),
m_netN(0U),
m_networkWatchdog(1000U, 0U, 1500U),
m_rfTimeoutTimer(1000U, timeout),
@ -529,6 +530,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
}
} else if (audioSync) {
if (m_rfState == RS_RF_AUDIO) {
m_lastrfN = 0;
// Convert the Audio Sync to be from the BS or MS as needed
CSync::addDMRAudioSync(data + 2U, m_duplex);
@ -574,6 +576,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
m_rfN = data[1U] & 0x0FU;
if (m_rfN > 5U)
return false;
if (m_rfN == m_lastrfN)
return false;
if (m_rfN != (m_lastrfN + 1U))
return false;
m_lastrfN = m_rfN;
unsigned int errors = 0U;
unsigned char fid = m_rfLC->getFID();
@ -816,6 +823,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
m_rfN = data[1U] & 0x0FU;
if (m_rfN > 5U)
return false;
if (m_rfN == m_lastrfN)
return false;
if (m_rfN != (m_lastrfN + 1U))
return false;
m_lastrfN = m_rfN;
// Regenerate the EMB
emb.getData(data + 2U);

View file

@ -79,6 +79,7 @@ private:
CDMRLC* m_netLC;
unsigned char m_rfSeqNo;
unsigned char m_rfN;
unsigned char m_lastrfN;
unsigned char m_netN;
CTimer m_networkWatchdog;
CTimer m_rfTimeoutTimer;

View file

@ -26,6 +26,7 @@
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
const unsigned int BUFFER_LENGTH = 100U;

View file

@ -25,9 +25,11 @@ const unsigned char MODE_DMR = 2U;
const unsigned char MODE_YSF = 3U;
const unsigned char MODE_P25 = 4U;
const unsigned char MODE_NXDN = 5U;
const unsigned char MODE_POCSAG = 6U;
const unsigned char MODE_CW = 98U;
const unsigned char MODE_LOCKOUT = 99U;
const unsigned char MODE_ERROR = 100U;
const unsigned char MODE_QUIT = 110U;
const unsigned char TAG_HEADER = 0x00U;
const unsigned char TAG_DATA = 0x01U;
@ -41,6 +43,7 @@ enum HW_TYPE {
HWT_MMDVM_HS_HAT,
HWT_MMDVM_HS_DUAL_HAT,
HWT_NANO_HOTSPOT,
HWT_NANO_DV,
HWT_MMDVM_HS,
HWT_UNKNOWN
};

View file

@ -18,8 +18,25 @@
#include "Display.h"
#include "Defines.h"
#include "SerialController.h"
#include "ModemSerialPort.h"
#include "NullDisplay.h"
#include "TFTSerial.h"
#include "LCDproc.h"
#include "Nextion.h"
#include "Conf.h"
#include "Modem.h"
#include "UMP.h"
#include "Log.h"
#if defined(HD44780)
#include "HD44780.h"
#endif
#if defined(OLED)
#include "OLED.h"
#endif
#include <cstdio>
#include <cassert>
#include <cstring>
@ -71,6 +88,17 @@ void CDisplay::setError(const char* text)
setErrorInt(text);
}
void CDisplay::setQuit()
{
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_QUIT;
m_mode2 = MODE_QUIT;
setQuitInt();
}
void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
@ -298,6 +326,25 @@ void CDisplay::clearNXDN()
}
}
void CDisplay::writePOCSAG(uint32_t ric, const std::string& message)
{
m_timer1.start();
m_mode1 = MODE_POCSAG;
writePOCSAGInt(ric, message);
}
void CDisplay::clearPOCSAG()
{
if (m_timer1.hasExpired()) {
clearPOCSAGInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_POCSAG;
}
}
void CDisplay::writeCW()
{
m_timer1.start();
@ -336,6 +383,11 @@ void CDisplay::clock(unsigned int ms)
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_POCSAG:
clearPOCSAGInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_CW:
clearCWInt();
m_mode1 = MODE_IDLE;
@ -406,3 +458,159 @@ void CDisplay::writeNXDNRSSIInt(unsigned char rssi)
void CDisplay::writeNXDNBERInt(float ber)
{
}
/* Factory method extracted from MMDVMHost.cpp - BG5HHP */
CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem)
{
CDisplay *display = NULL;
std::string type = conf.getDisplay();
unsigned int dmrid = conf.getDMRId();
LogInfo("Display Parameters");
LogInfo(" Type: %s", type.c_str());
if (type == "TFT Serial") {
std::string port = conf.getTFTSerialPort();
unsigned int brightness = conf.getTFTSerialBrightness();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
ISerialPort* serial = NULL;
if (port == "modem")
serial = new CModemSerialPort(modem);
else
serial = new CSerialController(port, SERIAL_9600);
display = new CTFTSerial(conf.getCallsign(), dmrid, serial, brightness);
} else if (type == "Nextion") {
std::string port = conf.getNextionPort();
unsigned int brightness = conf.getNextionBrightness();
bool displayClock = conf.getNextionDisplayClock();
bool utc = conf.getNextionUTC();
unsigned int idleBrightness = conf.getNextionIdleBrightness();
unsigned int screenLayout = conf.getNextionScreenLayout();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
LogInfo(" Idle Brightness: %u", idleBrightness);
switch (screenLayout) {
case 0U:
LogInfo(" Screen Layout: G4KLX (Default)");
break;
case 2U:
LogInfo(" Screen Layout: ON7LDS");
break;
case 3U:
LogInfo(" Screen Layout: DIY by ON7LDS");
break;
case 4U:
LogInfo(" Screen Layout: DIY by ON7LDS (High speed)");
break;
default:
LogInfo(" Screen Layout: %u (Unknown)", screenLayout);
break;
}
if (port == "modem") {
ISerialPort* serial = new CModemSerialPort(modem);
display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout);
} else if (port == "ump") {
if (ump != NULL)
display = new CNextion(conf.getCallsign(), dmrid, ump, brightness, displayClock, utc, idleBrightness, screenLayout);
else
display = new CNullDisplay;
} else {
SERIAL_SPEED baudrate = SERIAL_9600;
if (screenLayout==4U)
baudrate = SERIAL_115200;
ISerialPort* serial = new CSerialController(port, baudrate);
display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout);
}
} else if (type == "LCDproc") {
std::string address = conf.getLCDprocAddress();
unsigned int port = conf.getLCDprocPort();
unsigned int localPort = conf.getLCDprocLocalPort();
bool displayClock = conf.getLCDprocDisplayClock();
bool utc = conf.getLCDprocUTC();
bool dimOnIdle = conf.getLCDprocDimOnIdle();
LogInfo(" Address: %s", address.c_str());
LogInfo(" Port: %u", port);
if (localPort == 0 )
LogInfo(" Local Port: random");
else
LogInfo(" Local Port: %u", localPort);
LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no");
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
display = new CLCDproc(address.c_str(), port, localPort, conf.getCallsign(), dmrid, displayClock, utc, conf.getDuplex(), dimOnIdle);
#if defined(HD44780)
} else if (type == "HD44780") {
unsigned int rows = conf.getHD44780Rows();
unsigned int columns = conf.getHD44780Columns();
std::vector<unsigned int> pins = conf.getHD44780Pins();
unsigned int i2cAddress = conf.getHD44780i2cAddress();
bool pwm = conf.getHD44780PWM();
unsigned int pwmPin = conf.getHD44780PWMPin();
unsigned int pwmBright = conf.getHD44780PWMBright();
unsigned int pwmDim = conf.getHD44780PWMDim();
bool displayClock = conf.getHD44780DisplayClock();
bool utc = conf.getHD44780UTC();
if (pins.size() == 6U) {
LogInfo(" Rows: %u", rows);
LogInfo(" Columns: %u", columns);
#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY)
LogInfo(" Device Address: %#x", i2cAddress);
#else
LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U));
#endif
LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no");
if (pwm) {
LogInfo(" PWM Pin: %u", pwmPin);
LogInfo(" PWM Bright: %u", pwmBright);
LogInfo(" PWM Dim: %u", pwmDim);
}
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
display = new CHD44780(rows, columns, conf.getCallsign(), dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, conf.getDuplex());
}
#endif
#if defined(OLED)
} else if (type == "OLED") {
unsigned char type = conf.getOLEDType();
unsigned char brightness = conf.getOLEDBrightness();
bool invert = conf.getOLEDInvert();
bool scroll = conf.getOLEDScroll();
display = new COLED(type, brightness, invert, scroll, conf.getDMRNetworkSlot1(), conf.getDMRNetworkSlot2());
#endif
} else {
LogWarning("No valid display found, disabling");
display = new CNullDisplay;
}
bool ret = display->open();
if (!ret) {
delete display;
display = new CNullDisplay;
}
return display;
}

View file

@ -23,6 +23,12 @@
#include <string>
#include <cstdint>
class CConf;
class CModem;
class CUMP;
class CDisplay
{
public:
@ -34,6 +40,7 @@ public:
void setIdle();
void setLockout();
void setError(const char* text);
void setQuit();
void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
void writeDStarRSSI(unsigned char rssi);
@ -61,17 +68,22 @@ public:
void writeNXDNBER(float ber);
void clearNXDN();
void writePOCSAG(uint32_t ric, const std::string& message);
void clearPOCSAG();
void writeCW();
void clearCW();
virtual void close() = 0;
void clock(unsigned int ms);
static CDisplay* createDisplay(const CConf& conf, CUMP* ump, CModem* modem);
protected:
virtual void setIdleInt() = 0;
virtual void setLockoutInt() = 0;
virtual void setErrorInt(const char* text) = 0;
virtual void setQuitInt() = 0;
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) = 0;
virtual void writeDStarRSSIInt(unsigned char rssi);
@ -99,6 +111,9 @@ protected:
virtual void writeNXDNBERInt(float ber);
virtual void clearNXDNInt() = 0;
virtual void writePOCSAGInt(uint32_t ric, const std::string& message) = 0;
virtual void clearPOCSAGInt() = 0;
virtual void writeCWInt() = 0;
virtual void clearCWInt() = 0;

View file

@ -374,6 +374,31 @@ void CHD44780::setLockoutInt()
m_dmr = false;
}
void CHD44780::setQuitInt()
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
::lcdPosition(m_fd, 0, 0);
::lcdPuts(m_fd, "MMDVM");
::lcdPosition(m_fd, 0, 1);
::lcdPuts(m_fd, "STOPPED");
m_dmr = false;
}
void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
@ -995,6 +1020,18 @@ void CHD44780::clearNXDNInt()
}
}
void CHD44780::writePOCSAGInt(uint32_t ric, const std::string& message)
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, "POCSAG TX");
}
void CHD44780::clearPOCSAGInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, " Idle");
}
void CHD44780::writeCWInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);

View file

@ -100,6 +100,7 @@ protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
@ -121,6 +122,9 @@ protected:
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

165
I2CController.cpp Normal file
View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2002-2004,2007-2011,2013,2014-2017 by Jonathan Naylor G4KLX
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
*
* 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 "I2CController.h"
#include "Log.h"
#include <cstring>
#include <cassert>
#include <sys/types.h>
#if defined(_WIN32) || defined(_WIN64)
#include <setupapi.h>
#include <winioctl.h>
CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) :
CSerialController(device, speed, assertRTS),
m_address(address)
{
}
CI2CController::~CI2CController()
{
}
bool CI2CController::open()
{
return CSerialController::open();
}
int CI2CController::read(unsigned char* buffer, unsigned int length)
{
return CSerialController::read(buffer, length);
}
int CI2CController::write(const unsigned char* buffer, unsigned int length)
{
return CSerialController::write(buffer, length);
}
#else
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#if !defined(__APPLE__)
#include <linux/i2c-dev.h>
#endif
CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) :
CSerialController(device, speed, assertRTS),
m_address(address)
{
}
CI2CController::~CI2CController()
{
}
bool CI2CController::open()
{
assert(m_fd == -1);
#if !defined(__APPLE__)
m_fd = ::open(m_device.c_str(), O_RDWR);
if (m_fd < 0) {
LogError("Cannot open device - %s", m_device.c_str());
return false;
}
if (::ioctl(m_fd, I2C_TENBIT, 0) < 0) {
LogError("CI2C: failed to set 7bitaddress");
::close(m_fd);
return false;
}
if (::ioctl(m_fd, I2C_SLAVE, m_address) < 0) {
LogError("CI2C: Failed to acquire bus access/talk to slave 0x%02X", m_address);
::close(m_fd);
return false;
}
#else
#warning "I2C controller does not support OSX"
#endif
return true;
}
int CI2CController::read(unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(m_fd != -1);
if (length == 0U)
return 0;
unsigned int offset = 0U;
while (offset < length) {
#if !defined(__APPLE__)
ssize_t n = ::read(m_fd, buffer + offset, 1U);
if (n < 0) {
if (errno != EAGAIN) {
LogError("Error returned from read(), errno=%d", errno);
return -1;
}
}
if (n > 0)
offset += n;
#endif
}
return length;
}
int CI2CController::write(const unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(m_fd != -1);
if (length == 0U)
return 0;
unsigned int ptr = 0U;
while (ptr < length) {
ssize_t n = 0U;
#if !defined(__APPLE__)
n = ::write(m_fd, buffer + ptr, 1U);
#endif
if (n < 0) {
if (errno != EAGAIN) {
LogError("Error returned from write(), errno=%d", errno);
return -1;
}
}
if (n > 0)
ptr += n;
}
return length;
}
#endif

40
I2CController.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
*
* 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 I2CController_H
#define I2CController_H
#include "SerialController.h"
class CI2CController : public CSerialController {
public:
CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address = 0x22U, bool assertRTS = false);
virtual ~CI2CController();
virtual bool open();
virtual int read(unsigned char* buffer, unsigned int length);
virtual int write(const unsigned char* buffer, unsigned int length);
private:
unsigned int m_address;
};
#endif

BIN
Images/POCSAG.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -224,6 +224,23 @@ void CLCDproc::setLockoutInt()
// LED 4 Green 8 Red 128 Yellow 136
void CLCDproc::setQuitInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Stopped", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
@ -514,6 +531,14 @@ void CLCDproc::clearNXDNInt()
socketPrintf(m_socketfd, "output 16"); // Set LED5 color green
}
void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message)
{
}
void CLCDproc::clearPOCSAGInt()
{
}
void CLCDproc::writeCWInt()
{
}

View file

@ -39,6 +39,8 @@ protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
@ -60,6 +62,9 @@ protected:
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

View file

@ -44,6 +44,8 @@ Time=24
# Port=/dev/ttyACM0
# Port=/dev/ttyAMA0
Port=\\.\COM3
Protocol=uart
# Address=0x22
TXInvert=1
RXInvert=0
PTTInvert=0
@ -62,6 +64,7 @@ RFLevel=100
# YSFTXLevel=50
# P25TXLevel=50
# NXDNTXLevel=50
# POCSAGTXLevel=50
RSSIMappingFile=RSSI.dat
Trace=0
Debug=0
@ -71,6 +74,7 @@ Enable=0
RemoteAddress=127.0.0.1
RemotePort=40094
LocalPort=40095
# SendFrameType=0
[UMP]
Enable=0
@ -107,6 +111,7 @@ TXHang=4
Enable=1
LowDeviation=0
SelfOnly=0
TXHang=4
#DGID=1
RemoteGateway=0
# ModeHang=10
@ -126,6 +131,10 @@ SelfOnly=0
RemoteGateway=0
# ModeHang=10
[POCSAG]
Enable=1
Frequency=439987500
[D-Star Network]
Enable=1
GatewayAddress=127.0.0.1
@ -173,6 +182,15 @@ GatewayPort=14020
# ModeHang=3
Debug=0
[POCSAG Network]
Enable=1
LocalAddress=127.0.0.1
LocalPort=3800
GatewayAddress=127.0.0.1
GatewayPort=4800
# ModeHang=3
Debug=0
[TFT Serial]
# Port=modem
Port=/dev/ttyAMA0

View file

@ -19,34 +19,24 @@
#include "MMDVMHost.h"
#include "RSSIInterpolator.h"
#include "SerialController.h"
#include "ModemSerialPort.h"
#include "Version.h"
#include "StopWatch.h"
#include "Defines.h"
#include "DStarControl.h"
#include "DMRControl.h"
#include "TFTSerial.h"
#include "NullDisplay.h"
#include "YSFControl.h"
#include "P25Control.h"
#include "NXDNControl.h"
#include "Nextion.h"
#include "LCDproc.h"
#include "POCSAGControl.h"
#include "Thread.h"
#include "Log.h"
#include "GitVersion.h"
#if defined(HD44780)
#include "HD44780.h"
#endif
#if defined(OLED)
#include "OLED.h"
#endif
#include <cstdio>
#include <vector>
#include <cstdlib>
#if !defined(_WIN32) && !defined(_WIN64)
#include <sys/types.h>
#include <unistd.h>
@ -134,6 +124,7 @@ m_dmrNetwork(NULL),
m_ysfNetwork(NULL),
m_p25Network(NULL),
m_nxdnNetwork(NULL),
m_pocsagNetwork(NULL),
m_display(NULL),
m_ump(NULL),
m_mode(MODE_IDLE),
@ -147,6 +138,7 @@ m_dmrNetModeHang(3U),
m_ysfNetModeHang(3U),
m_p25NetModeHang(3U),
m_nxdnNetModeHang(3U),
m_pocsagNetModeHang(3U),
m_modeTimer(1000U),
m_dmrTXTimer(1000U),
m_cwIdTimer(1000U),
@ -157,6 +149,7 @@ m_dmrEnabled(false),
m_ysfEnabled(false),
m_p25Enabled(false),
m_nxdnEnabled(false),
m_pocsagEnabled(false),
m_cwIdTime(0U),
m_dmrLookup(NULL),
m_nxdnLookup(NULL),
@ -178,19 +171,13 @@ int CMMDVMHost::run()
return 1;
}
ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel());
if (!ret) {
::fprintf(stderr, "MMDVMHost: unable to open the log file\n");
return 1;
}
#if !defined(_WIN32) && !defined(_WIN64)
bool m_daemon = m_conf.getDaemon();
if (m_daemon) {
// Create new process
pid_t pid = ::fork();
if (pid == -1) {
LogWarning("Couldn't fork() , exiting");
::fprintf(stderr, "Couldn't fork() , exiting\n");
return -1;
} else if (pid != 0) {
exit(EXIT_SUCCESS);
@ -198,56 +185,66 @@ int CMMDVMHost::run()
// Create new session and process group
if (::setsid() == -1){
LogWarning("Couldn't setsid(), exiting");
::fprintf(stderr, "Couldn't setsid(), exiting\n");
return -1;
}
// Set the working directory to the root directory
if (::chdir("/") == -1){
LogWarning("Couldn't cd /, exiting");
::fprintf(stderr, "Couldn't cd /, exiting\n");
return -1;
}
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
#if !defined(HD44780) && !defined(OLED)
//If we are currently root...
#if !defined(HD44780) && !defined(OLED) && !defined(_OPENWRT)
// If we are currently root...
if (getuid() == 0) {
struct passwd* user = ::getpwnam("mmdvm");
if (user == NULL) {
LogError("Could not get the mmdvm user, exiting");
::fprintf(stderr, "Could not get the mmdvm user, exiting\n");
return -1;
}
uid_t mmdvm_uid = user->pw_uid;
gid_t mmdvm_gid = user->pw_gid;
//Set user and group ID's to mmdvm:mmdvm
// Set user and group ID's to mmdvm:mmdvm
if (::setgid(mmdvm_gid) != 0) {
LogWarning("Could not set mmdvm GID, exiting");
::fprintf(stderr, "Could not set mmdvm GID, exiting\n");
return -1;
}
if (::setuid(mmdvm_uid) != 0) {
LogWarning("Could not set mmdvm UID, exiting");
::fprintf(stderr, "Could not set mmdvm UID, exiting\n");
return -1;
}
//Double check it worked (AKA Paranoia)
// Double check it worked (AKA Paranoia)
if (::setuid(0) != -1){
LogWarning("It's possible to regain root - something is wrong!, exiting");
::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n");
return -1;
}
}
}
#else
LogWarning("Dropping root permissions in daemon mode is disabled with HD44780 display");
::fprintf(stderr, "Dropping root permissions in daemon mode is disabled.\n");
}
#endif
#endif
ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel());
if (!ret) {
::fprintf(stderr, "MMDVMHost: unable to open the log file\n");
return 1;
}
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon) {
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
}
#endif
LogInfo(HEADER1);
LogInfo(HEADER2);
LogInfo(HEADER3);
@ -276,7 +273,7 @@ int CMMDVMHost::run()
}
}
createDisplay();
m_display = CDisplay::createDisplay(m_conf,m_ump,m_modem);
if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) {
ret = createDStarNetwork();
@ -308,19 +305,28 @@ int CMMDVMHost::run()
return 1;
}
if (m_pocsagEnabled && m_conf.getPOCSAGNetworkEnabled()) {
ret = createPOCSAGNetwork();
if (!ret)
return 1;
}
in_addr transparentAddress;
unsigned int transparentPort = 0U;
CUDPSocket* transparentSocket = NULL;
unsigned int sendFrameType = 0U;
if (m_conf.getTransparentEnabled()) {
std::string remoteAddress = m_conf.getTransparentRemoteAddress();
unsigned int remotePort = m_conf.getTransparentRemotePort();
unsigned int localPort = m_conf.getTransparentLocalPort();
sendFrameType = m_conf.getTransparentSendFrameType();
LogInfo("Transparent Data");
LogInfo(" Remote Address: %s", remoteAddress.c_str());
LogInfo(" Remote Port: %u", remotePort);
LogInfo(" Local Port: %u", localPort);
LogInfo(" Send Frame Type: %u", sendFrameType);
transparentAddress = CUDPSocket::lookup(remoteAddress);
transparentPort = remotePort;
@ -440,7 +446,7 @@ int CMMDVMHost::run()
LogInfo(" Embedded LC Only: %s", embeddedLCOnly ? "yes" : "no");
LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no");
LogInfo(" Prefixes: %u", prefixes.size());
if (blackList.size() > 0U)
LogInfo(" Source ID Black List: %u", blackList.size());
if (whiteList.size() > 0U)
@ -474,16 +480,18 @@ int CMMDVMHost::run()
CYSFControl* ysf = NULL;
if (m_ysfEnabled) {
bool lowDeviation = m_conf.getFusionLowDeviation();
bool remoteGateway = m_conf.getFusionRemoteGateway();
bool selfOnly = m_conf.getFusionSelfOnly();
bool sqlEnabled = m_conf.getFusionSQLEnabled();
unsigned char sql = m_conf.getFusionSQL();
m_ysfRFModeHang = m_conf.getFusionModeHang();
bool lowDeviation = m_conf.getFusionLowDeviation();
bool remoteGateway = m_conf.getFusionRemoteGateway();
unsigned int txHang = m_conf.getFusionTXHang();
bool selfOnly = m_conf.getFusionSelfOnly();
bool sqlEnabled = m_conf.getFusionSQLEnabled();
unsigned char sql = m_conf.getFusionSQL();
m_ysfRFModeHang = m_conf.getFusionModeHang();
LogInfo("YSF RF Parameters");
LogInfo(" Low Deviation: %s", lowDeviation ? "yes" : "no");
LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no");
LogInfo(" TX Hang: %us", txHang);
LogInfo(" Self Only: %s", selfOnly ? "yes" : "no");
LogInfo(" DSQ: %s", sqlEnabled ? "yes" : "no");
if (sqlEnabled)
@ -543,6 +551,20 @@ int CMMDVMHost::run()
nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_display, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi);
}
CTimer pocsagTimer(1000U, 30U);
CPOCSAGControl* pocsag = NULL;
if (m_pocsagEnabled) {
unsigned int frequency = m_conf.getPOCSAGFrequency();
LogInfo("POCSAG RF Parameters");
LogInfo(" Frequency: %uHz", frequency);
pocsag = new CPOCSAGControl(m_pocsagNetwork, m_display);
pocsagTimer.start();
}
setMode(MODE_IDLE);
LogMessage("MMDVMHost-%s is running", VERSION);
@ -841,12 +863,31 @@ int CMMDVMHost::run()
}
}
if (pocsag != NULL) {
ret = m_modem->hasPOCSAGSpace();
if (ret) {
len = pocsag->readModem(data);
if (len > 0U) {
if (m_mode == MODE_IDLE) {
m_modeTimer.setTimeout(m_pocsagNetModeHang);
setMode(MODE_POCSAG);
}
if (m_mode == MODE_POCSAG) {
m_modem->writePOCSAGData(data, len);
m_modeTimer.start();
} else if (m_mode != MODE_LOCKOUT) {
LogWarning("POCSAG data received when in mode %u", m_mode);
}
}
}
}
if (transparentSocket != NULL) {
in_addr address;
unsigned int port = 0U;
len = transparentSocket->read(data, 200U, address, port);
if (len > 0U)
m_modem->writeTransparentData(data, len);
m_modem->writeTransparentData(data, len, sendFrameType);
}
unsigned int ms = stopWatch.elapsed();
@ -867,6 +908,8 @@ int CMMDVMHost::run()
p25->clock(ms);
if (nxdn != NULL)
nxdn->clock(ms);
if (pocsag != NULL)
pocsag->clock(ms);
if (m_dstarNetwork != NULL)
m_dstarNetwork->clock(ms);
@ -878,6 +921,8 @@ int CMMDVMHost::run()
m_p25Network->clock(ms);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->clock(ms);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->clock(ms);
m_cwIdTimer.clock(ms);
if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) {
@ -912,6 +957,13 @@ int CMMDVMHost::run()
m_dmrTXTimer.stop();
}
pocsagTimer.clock(ms);
if (pocsagTimer.isRunning() && pocsagTimer.hasExpired()) {
assert(m_pocsagNetwork != NULL);
m_pocsagNetwork->enable(m_mode == MODE_IDLE || m_mode == MODE_POCSAG);
pocsagTimer.start();
}
if (m_ump != NULL)
m_ump->clock(ms);
@ -919,7 +971,7 @@ int CMMDVMHost::run()
CThread::sleep(5U);
}
setMode(MODE_IDLE);
setMode(MODE_QUIT);
m_modem->close();
delete m_modem;
@ -963,6 +1015,11 @@ int CMMDVMHost::run()
delete m_nxdnNetwork;
}
if (m_pocsagNetwork != NULL) {
m_pocsagNetwork->close();
delete m_pocsagNetwork;
}
if (transparentSocket != NULL) {
transparentSocket->close();
delete transparentSocket;
@ -973,39 +1030,48 @@ int CMMDVMHost::run()
delete ysf;
delete p25;
delete nxdn;
delete pocsag;
return 0;
}
bool CMMDVMHost::createModem()
{
std::string port = m_conf.getModemPort();
bool rxInvert = m_conf.getModemRXInvert();
bool txInvert = m_conf.getModemTXInvert();
bool pttInvert = m_conf.getModemPTTInvert();
unsigned int txDelay = m_conf.getModemTXDelay();
unsigned int dmrDelay = m_conf.getModemDMRDelay();
float rxLevel = m_conf.getModemRXLevel();
float cwIdTXLevel = m_conf.getModemCWIdTXLevel();
float dstarTXLevel = m_conf.getModemDStarTXLevel();
float dmrTXLevel = m_conf.getModemDMRTXLevel();
float ysfTXLevel = m_conf.getModemYSFTXLevel();
float p25TXLevel = m_conf.getModemP25TXLevel();
float nxdnTXLevel = m_conf.getModemNXDNTXLevel();
bool trace = m_conf.getModemTrace();
bool debug = m_conf.getModemDebug();
unsigned int colorCode = m_conf.getDMRColorCode();
bool lowDeviation = m_conf.getFusionLowDeviation();
unsigned int rxFrequency = m_conf.getRXFrequency();
unsigned int txFrequency = m_conf.getTXFrequency();
int rxOffset = m_conf.getModemRXOffset();
int txOffset = m_conf.getModemTXOffset();
int rxDCOffset = m_conf.getModemRXDCOffset();
int txDCOffset = m_conf.getModemTXDCOffset();
float rfLevel = m_conf.getModemRFLevel();
std::string port = m_conf.getModemPort();
std::string protocol = m_conf.getModemProtocol();
unsigned int address = m_conf.getModemAddress();
bool rxInvert = m_conf.getModemRXInvert();
bool txInvert = m_conf.getModemTXInvert();
bool pttInvert = m_conf.getModemPTTInvert();
unsigned int txDelay = m_conf.getModemTXDelay();
unsigned int dmrDelay = m_conf.getModemDMRDelay();
float rxLevel = m_conf.getModemRXLevel();
float cwIdTXLevel = m_conf.getModemCWIdTXLevel();
float dstarTXLevel = m_conf.getModemDStarTXLevel();
float dmrTXLevel = m_conf.getModemDMRTXLevel();
float ysfTXLevel = m_conf.getModemYSFTXLevel();
float p25TXLevel = m_conf.getModemP25TXLevel();
float nxdnTXLevel = m_conf.getModemNXDNTXLevel();
float pocsagTXLevel = m_conf.getModemPOCSAGTXLevel();
bool trace = m_conf.getModemTrace();
bool debug = m_conf.getModemDebug();
unsigned int colorCode = m_conf.getDMRColorCode();
bool lowDeviation = m_conf.getFusionLowDeviation();
unsigned int txHang = m_conf.getFusionTXHang();
unsigned int rxFrequency = m_conf.getRXFrequency();
unsigned int txFrequency = m_conf.getTXFrequency();
unsigned int pocsagFrequency = m_conf.getPOCSAGFrequency();
int rxOffset = m_conf.getModemRXOffset();
int txOffset = m_conf.getModemTXOffset();
int rxDCOffset = m_conf.getModemRXDCOffset();
int txDCOffset = m_conf.getModemTXDCOffset();
float rfLevel = m_conf.getModemRFLevel();
LogInfo("Modem Parameters");
LogInfo(" Port: %s", port.c_str());
LogInfo(" Protocol: %s", protocol.c_str());
if (protocol == "i2c")
LogInfo(" i2c Address: %02X", address);
LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no");
LogInfo(" TX Invert: %s", txInvert ? "yes" : "no");
LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no");
@ -1023,15 +1089,17 @@ bool CMMDVMHost::createModem()
LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel);
LogInfo(" P25 TX Level: %.1f%%", p25TXLevel);
LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel);
LogInfo(" POCSAG TX Level: %.1f%%", pocsagTXLevel);
LogInfo(" RX Frequency: %uHz (%uHz)", rxFrequency, rxFrequency + rxOffset);
LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset);
m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug);
m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled);
m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel);
m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel);
m_modem->setSerialParams(protocol,address);
m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled);
m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel);
m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency);
m_modem->setDMRParams(colorCode);
m_modem->setYSFParams(lowDeviation);
m_modem->setYSFParams(lowDeviation, txHang);
bool ret = m_modem->open();
if (!ret) {
@ -1230,17 +1298,48 @@ bool CMMDVMHost::createNXDNNetwork()
return true;
}
bool CMMDVMHost::createPOCSAGNetwork()
{
std::string gatewayAddress = m_conf.getPOCSAGGatewayAddress();
unsigned int gatewayPort = m_conf.getPOCSAGGatewayPort();
std::string localAddress = m_conf.getPOCSAGLocalAddress();
unsigned int localPort = m_conf.getPOCSAGLocalPort();
m_pocsagNetModeHang = m_conf.getPOCSAGNetworkModeHang();
bool debug = m_conf.getPOCSAGNetworkDebug();
LogInfo("POCSAG 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);
LogInfo(" Mode Hang: %us", m_pocsagNetModeHang);
m_pocsagNetwork = new CPOCSAGNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug);
bool ret = m_pocsagNetwork->open();
if (!ret) {
delete m_pocsagNetwork;
m_pocsagNetwork = NULL;
return false;
}
m_pocsagNetwork->enable(true);
return true;
}
void CMMDVMHost::readParams()
{
m_dstarEnabled = m_conf.getDStarEnabled();
m_dmrEnabled = m_conf.getDMREnabled();
m_ysfEnabled = m_conf.getFusionEnabled();
m_p25Enabled = m_conf.getP25Enabled();
m_nxdnEnabled = m_conf.getNXDNEnabled();
m_duplex = m_conf.getDuplex();
m_callsign = m_conf.getCallsign();
m_id = m_conf.getId();
m_timeout = m_conf.getTimeout();
m_dstarEnabled = m_conf.getDStarEnabled();
m_dmrEnabled = m_conf.getDMREnabled();
m_ysfEnabled = m_conf.getFusionEnabled();
m_p25Enabled = m_conf.getP25Enabled();
m_nxdnEnabled = m_conf.getNXDNEnabled();
m_pocsagEnabled = m_conf.getPOCSAGEnabled();
m_duplex = m_conf.getDuplex();
m_callsign = m_conf.getCallsign();
m_id = m_conf.getId();
m_timeout = m_conf.getTimeout();
LogInfo("General Parameters");
LogInfo(" Callsign: %s", m_callsign.c_str());
@ -1252,160 +1351,7 @@ void CMMDVMHost::readParams()
LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled");
LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled");
LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled");
}
void CMMDVMHost::createDisplay()
{
std::string type = m_conf.getDisplay();
unsigned int dmrid = m_conf.getDMRId();
LogInfo("Display Parameters");
LogInfo(" Type: %s", type.c_str());
if (type == "TFT Serial") {
std::string port = m_conf.getTFTSerialPort();
unsigned int brightness = m_conf.getTFTSerialBrightness();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
ISerialPort* serial = NULL;
if (port == "modem")
serial = new CModemSerialPort(m_modem);
else
serial = new CSerialController(port, SERIAL_9600);
m_display = new CTFTSerial(m_callsign, dmrid, serial, brightness);
} else if (type == "Nextion") {
std::string port = m_conf.getNextionPort();
unsigned int brightness = m_conf.getNextionBrightness();
bool displayClock = m_conf.getNextionDisplayClock();
bool utc = m_conf.getNextionUTC();
unsigned int idleBrightness = m_conf.getNextionIdleBrightness();
unsigned int screenLayout = m_conf.getNextionScreenLayout();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
LogInfo(" Idle Brightness: %u", idleBrightness);
switch (screenLayout) {
case 0U:
LogInfo(" Screen Layout: G4KLX (Default)");
break;
case 2U:
LogInfo(" Screen Layout: ON7LDS");
break;
case 3U:
LogInfo(" Screen Layout: DIY by ON7LDS");
break;
case 4U:
LogInfo(" Screen Layout: DIY by ON7LDS (High speed)");
break;
default:
LogInfo(" Screen Layout: %u (Unknown)", screenLayout);
break;
}
if (port == "modem") {
ISerialPort* serial = new CModemSerialPort(m_modem);
m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout);
} else if (port == "ump") {
if (m_ump != NULL)
m_display = new CNextion(m_callsign, dmrid, m_ump, brightness, displayClock, utc, idleBrightness, screenLayout);
} else {
SERIAL_SPEED baudrate = SERIAL_9600;
if (screenLayout==4U)
baudrate = SERIAL_115200;
ISerialPort* serial = new CSerialController(port, baudrate);
m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout);
}
} else if (type == "LCDproc") {
std::string address = m_conf.getLCDprocAddress();
unsigned int port = m_conf.getLCDprocPort();
unsigned int localPort = m_conf.getLCDprocLocalPort();
bool displayClock = m_conf.getLCDprocDisplayClock();
bool utc = m_conf.getLCDprocUTC();
bool dimOnIdle = m_conf.getLCDprocDimOnIdle();
LogInfo(" Address: %s", address.c_str());
LogInfo(" Port: %u", port);
if (localPort == 0 )
LogInfo(" Local Port: random");
else
LogInfo(" Local Port: %u", localPort);
LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no");
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
m_display = new CLCDproc(address.c_str(), port, localPort, m_callsign, dmrid, displayClock, utc, m_duplex, dimOnIdle);
#if defined(HD44780)
} else if (type == "HD44780") {
unsigned int rows = m_conf.getHD44780Rows();
unsigned int columns = m_conf.getHD44780Columns();
std::vector<unsigned int> pins = m_conf.getHD44780Pins();
unsigned int i2cAddress = m_conf.getHD44780i2cAddress();
bool pwm = m_conf.getHD44780PWM();
unsigned int pwmPin = m_conf.getHD44780PWMPin();
unsigned int pwmBright = m_conf.getHD44780PWMBright();
unsigned int pwmDim = m_conf.getHD44780PWMDim();
bool displayClock = m_conf.getHD44780DisplayClock();
bool utc = m_conf.getHD44780UTC();
if (pins.size() == 6U) {
LogInfo(" Rows: %u", rows);
LogInfo(" Columns: %u", columns);
#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY)
LogInfo(" Device Address: %#x", i2cAddress);
#else
LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U));
#endif
LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no");
if (pwm) {
LogInfo(" PWM Pin: %u", pwmPin);
LogInfo(" PWM Bright: %u", pwmBright);
LogInfo(" PWM Dim: %u", pwmDim);
}
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
m_display = new CHD44780(rows, columns, m_callsign, dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, m_duplex);
}
#endif
#if defined(OLED)
} else if (type == "OLED") {
unsigned char type = m_conf.getOLEDType();
unsigned char brightness = m_conf.getOLEDBrightness();
bool invert = m_conf.getOLEDInvert();
bool scroll = m_conf.getOLEDScroll();
m_display = new COLED(type, brightness, invert, scroll, m_conf.getDMRNetworkSlot1(), m_conf.getDMRNetworkSlot2());
#endif
} else {
m_display = new CNullDisplay;
}
if (m_display == NULL) {
LogWarning("No valid display found, disabling");
m_display = new CNullDisplay;
return;
}
bool ret = m_display->open();
if (!ret) {
delete m_display;
m_display = new CNullDisplay;
}
LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled");
}
void CMMDVMHost::setMode(unsigned char mode)
@ -1423,6 +1369,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
m_modem->setMode(MODE_DSTAR);
if (m_ump != NULL)
m_ump->setMode(MODE_DSTAR);
@ -1440,6 +1388,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
m_modem->setMode(MODE_DMR);
if (m_ump != NULL)
m_ump->setMode(MODE_DMR);
@ -1461,6 +1411,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
m_modem->setMode(MODE_YSF);
if (m_ump != NULL)
m_ump->setMode(MODE_YSF);
@ -1478,6 +1430,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_ysfNetwork->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
m_modem->setMode(MODE_P25);
if (m_ump != NULL)
m_ump->setMode(MODE_P25);
@ -1495,6 +1449,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_ysfNetwork->enable(false);
if (m_p25Network != NULL)
m_p25Network->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
m_modem->setMode(MODE_NXDN);
if (m_ump != NULL)
m_ump->setMode(MODE_NXDN);
@ -1503,6 +1459,25 @@ void CMMDVMHost::setMode(unsigned char mode)
m_cwIdTimer.stop();
break;
case MODE_POCSAG:
if (m_dstarNetwork != NULL)
m_dstarNetwork->enable(false);
if (m_dmrNetwork != NULL)
m_dmrNetwork->enable(false);
if (m_ysfNetwork != NULL)
m_ysfNetwork->enable(false);
if (m_p25Network != NULL)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
m_modem->setMode(MODE_POCSAG);
if (m_ump != NULL)
m_ump->setMode(MODE_POCSAG);
m_mode = MODE_POCSAG;
m_modeTimer.start();
m_cwIdTimer.stop();
break;
case MODE_LOCKOUT:
LogMessage("Mode set to Lockout");
if (m_dstarNetwork != NULL)
@ -1515,6 +1490,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) {
m_modem->writeDMRStart(false);
m_dmrTXTimer.stop();
@ -1540,6 +1517,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(false);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(false);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(false);
if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) {
m_modem->writeDMRStart(false);
m_dmrTXTimer.stop();
@ -1563,6 +1542,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_p25Network->enable(true);
if (m_nxdnNetwork != NULL)
m_nxdnNetwork->enable(true);
if (m_pocsagNetwork != NULL)
m_pocsagNetwork->enable(true);
if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) {
m_modem->writeDMRStart(false);
m_dmrTXTimer.stop();
@ -1579,6 +1560,9 @@ void CMMDVMHost::setMode(unsigned char mode)
m_cwIdTimer.start();
}
m_display->setIdle();
if (mode==MODE_QUIT) {
m_display->setQuit();
}
m_mode = MODE_IDLE;
m_modeTimer.stop();
break;

View file

@ -19,6 +19,7 @@
#if !defined(MMDVMHOST_H)
#define MMDVMHOST_H
#include "POCSAGNetwork.h"
#include "DStarNetwork.h"
#include "NXDNNetwork.h"
#include "NXDNLookup.h"
@ -43,42 +44,45 @@ public:
int run();
private:
CConf m_conf;
CModem* m_modem;
CDStarNetwork* m_dstarNetwork;
CDMRNetwork* m_dmrNetwork;
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
CNXDNNetwork* m_nxdnNetwork;
CDisplay* m_display;
CUMP* m_ump;
unsigned char m_mode;
unsigned int m_dstarRFModeHang;
unsigned int m_dmrRFModeHang;
unsigned int m_ysfRFModeHang;
unsigned int m_p25RFModeHang;
unsigned int m_nxdnRFModeHang;
unsigned int m_dstarNetModeHang;
unsigned int m_dmrNetModeHang;
unsigned int m_ysfNetModeHang;
unsigned int m_p25NetModeHang;
unsigned int m_nxdnNetModeHang;
CTimer m_modeTimer;
CTimer m_dmrTXTimer;
CTimer m_cwIdTimer;
bool m_duplex;
unsigned int m_timeout;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
bool m_nxdnEnabled;
unsigned int m_cwIdTime;
CDMRLookup* m_dmrLookup;
CNXDNLookup* m_nxdnLookup;
std::string m_callsign;
unsigned int m_id;
std::string m_cwCallsign;
CConf m_conf;
CModem* m_modem;
CDStarNetwork* m_dstarNetwork;
CDMRNetwork* m_dmrNetwork;
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
CNXDNNetwork* m_nxdnNetwork;
CPOCSAGNetwork* m_pocsagNetwork;
CDisplay* m_display;
CUMP* m_ump;
unsigned char m_mode;
unsigned int m_dstarRFModeHang;
unsigned int m_dmrRFModeHang;
unsigned int m_ysfRFModeHang;
unsigned int m_p25RFModeHang;
unsigned int m_nxdnRFModeHang;
unsigned int m_dstarNetModeHang;
unsigned int m_dmrNetModeHang;
unsigned int m_ysfNetModeHang;
unsigned int m_p25NetModeHang;
unsigned int m_nxdnNetModeHang;
unsigned int m_pocsagNetModeHang;
CTimer m_modeTimer;
CTimer m_dmrTXTimer;
CTimer m_cwIdTimer;
bool m_duplex;
unsigned int m_timeout;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
bool m_nxdnEnabled;
bool m_pocsagEnabled;
unsigned int m_cwIdTime;
CDMRLookup* m_dmrLookup;
CNXDNLookup* m_nxdnLookup;
std::string m_callsign;
unsigned int m_id;
std::string m_cwCallsign;
void readParams();
bool createModem();
@ -87,7 +91,7 @@ private:
bool createYSFNetwork();
bool createP25Network();
bool createNXDNNetwork();
void createDisplay();
bool createPOCSAGNetwork();
void setMode(unsigned char mode);
};

View file

@ -183,6 +183,7 @@
<ClInclude Include="Golay24128.h" />
<ClInclude Include="Hamming.h" />
<ClInclude Include="DMRLookup.h" />
<ClInclude Include="I2CController.h" />
<ClInclude Include="LCDproc.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="MMDVMHost.h" />
@ -212,6 +213,9 @@
<ClInclude Include="P25NID.h" />
<ClInclude Include="P25Trellis.h" />
<ClInclude Include="P25Utils.h" />
<ClInclude Include="POCSAGControl.h" />
<ClInclude Include="POCSAGDefines.h" />
<ClInclude Include="POCSAGNetwork.h" />
<ClInclude Include="QR1676.h" />
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="RS129.h" />
@ -266,6 +270,7 @@
<ClCompile Include="Golay2087.cpp" />
<ClCompile Include="Golay24128.cpp" />
<ClCompile Include="Hamming.cpp" />
<ClCompile Include="I2CController.cpp" />
<ClCompile Include="LCDproc.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="MMDVMHost.cpp" />
@ -294,6 +299,8 @@
<ClCompile Include="P25NID.cpp" />
<ClCompile Include="P25Trellis.cpp" />
<ClCompile Include="P25Utils.cpp" />
<ClCompile Include="POCSAGControl.cpp" />
<ClCompile Include="POCSAGNetwork.cpp" />
<ClCompile Include="QR1676.cpp" />
<ClCompile Include="RS129.cpp" />
<ClCompile Include="RS241213.cpp" />

View file

@ -263,6 +263,18 @@
<ClInclude Include="NXDNAudio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="POCSAGNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="POCSAGControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="POCSAGDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="I2CController.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BPTC19696.cpp">
@ -493,5 +505,14 @@
<ClCompile Include="NXDNAudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="POCSAGNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="POCSAGControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="I2CController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -9,11 +9,11 @@ LDFLAGS = -g
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \
NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
Nextion.o NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \
Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \
NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o \
QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
Nextion.o NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \
Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
Nextion.o NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \
Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
Nextion.o NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \
Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o \
Nextion.o NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \
Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

View file

@ -11,9 +11,9 @@ OBJECTS = \
DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \
NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o QR1676.o RS129.o RS241213.o \
RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \
YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o POCSAGNetwork.o \
QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost

269
Modem.cpp
View file

@ -16,11 +16,13 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "I2CController.h"
#include "DStarDefines.h"
#include "DMRDefines.h"
#include "YSFDefines.h"
#include "P25Defines.h"
#include "NXDNDefines.h"
#include "POCSAGDefines.h"
#include "Thread.h"
#include "Modem.h"
#include "Utils.h"
@ -71,6 +73,8 @@ const unsigned char MMDVM_P25_LOST = 0x32U;
const unsigned char MMDVM_NXDN_DATA = 0x40U;
const unsigned char MMDVM_NXDN_LOST = 0x41U;
const unsigned char MMDVM_POCSAG_DATA = 0x50U;
const unsigned char MMDVM_ACK = 0x70U;
const unsigned char MMDVM_NAK = 0x7FU;
@ -93,6 +97,7 @@ CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInver
m_port(port),
m_dmrColorCode(0U),
m_ysfLoDev(false),
m_ysfTXHang(4U),
m_duplex(duplex),
m_rxInvert(rxInvert),
m_txInvert(txInvert),
@ -106,18 +111,21 @@ m_dmrTXLevel(0U),
m_ysfTXLevel(0U),
m_p25TXLevel(0U),
m_nxdnTXLevel(0U),
m_pocsagTXLevel(0U),
m_trace(trace),
m_debug(debug),
m_rxFrequency(0U),
m_txFrequency(0U),
m_pocsagFrequency(0U),
m_dstarEnabled(false),
m_dmrEnabled(false),
m_ysfEnabled(false),
m_p25Enabled(false),
m_nxdnEnabled(false),
m_pocsagEnabled(false),
m_rxDCOffset(0),
m_txDCOffset(0),
m_serial(port, SERIAL_115200, true),
m_serial(NULL),
m_buffer(NULL),
m_length(0U),
m_offset(0U),
@ -133,6 +141,7 @@ m_rxP25Data(1000U, "Modem RX P25"),
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_rxTransparentData(1000U, "Modem RX Transparent"),
m_txTransparentData(1000U, "Modem TX Transparent"),
m_statusTimer(1000U, 0U, 250U),
@ -144,6 +153,7 @@ m_dmrSpace2(0U),
m_ysfSpace(0U),
m_p25Space(0U),
m_nxdnSpace(0U),
m_pocsagSpace(0U),
m_tx(false),
m_cd(false),
m_lockout(false),
@ -157,36 +167,49 @@ m_hwType(HWT_UNKNOWN)
CModem::~CModem()
{
delete m_serial;
delete[] m_buffer;
}
void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel)
void CModem::setSerialParams(const std::string& protocol, unsigned int address)
{
m_rxFrequency = rxFrequency + rxOffset;
m_txFrequency = txFrequency + txOffset;
m_txDCOffset = txDCOffset;
m_rxDCOffset = rxDCOffset;
m_rfLevel = rfLevel;
// Create the serial controller instance according the protocol specified in conf.
if (protocol == "i2c")
m_serial = new CI2CController(m_port, SERIAL_115200, address, true);
else
m_serial = new CSerialController(m_port, SERIAL_115200, true);
}
void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled)
void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency)
{
m_dstarEnabled = dstarEnabled;
m_dmrEnabled = dmrEnabled;
m_ysfEnabled = ysfEnabled;
m_p25Enabled = p25Enabled;
m_nxdnEnabled = nxdnEnabled;
m_rxFrequency = rxFrequency + rxOffset;
m_txFrequency = txFrequency + txOffset;
m_txDCOffset = txDCOffset;
m_rxDCOffset = rxDCOffset;
m_rfLevel = rfLevel;
m_pocsagFrequency = pocsagFrequency + txOffset;
}
void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel)
void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled)
{
m_rxLevel = rxLevel;
m_cwIdTXLevel = cwIdTXLevel;
m_dstarTXLevel = dstarTXLevel;
m_dmrTXLevel = dmrTXLevel;
m_ysfTXLevel = ysfTXLevel;
m_p25TXLevel = p25TXLevel;
m_nxdnTXLevel = nxdnTXLevel;
m_dstarEnabled = dstarEnabled;
m_dmrEnabled = dmrEnabled;
m_ysfEnabled = ysfEnabled;
m_p25Enabled = p25Enabled;
m_nxdnEnabled = nxdnEnabled;
m_pocsagEnabled = pocsagEnabled;
}
void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel)
{
m_rxLevel = rxLevel;
m_cwIdTXLevel = cwIdTXLevel;
m_dstarTXLevel = dstarTXLevel;
m_dmrTXLevel = dmrTXLevel;
m_ysfTXLevel = ysfTXLevel;
m_p25TXLevel = p25TXLevel;
m_nxdnTXLevel = nxdnTXLevel;
m_pocsagTXLevel = pocsagTXLevel;
}
void CModem::setDMRParams(unsigned int colorCode)
@ -196,22 +219,25 @@ void CModem::setDMRParams(unsigned int colorCode)
m_dmrColorCode = colorCode;
}
void CModem::setYSFParams(bool loDev)
void CModem::setYSFParams(bool loDev, unsigned int txHang)
{
m_ysfLoDev = loDev;
m_ysfLoDev = loDev;
m_ysfTXHang = txHang;
}
bool CModem::open()
{
::LogMessage("Opening the MMDVM");
bool ret = m_serial.open();
bool ret = m_serial->open();
if (!ret)
return false;
ret = readVersion();
if (!ret) {
m_serial.close();
m_serial->close();
delete m_serial;
m_serial = NULL;
return false;
} else {
/* Stopping the inactivity timer here when a firmware version has been
@ -221,13 +247,17 @@ bool CModem::open()
ret = setFrequency();
if (!ret) {
m_serial.close();
m_serial->close();
delete m_serial;
m_serial = NULL;
return false;
}
ret = setConfig();
if (!ret) {
m_serial.close();
m_serial->close();
delete m_serial;
m_serial = NULL;
return false;
}
@ -241,6 +271,8 @@ bool CModem::open()
void CModem::clock(unsigned int ms)
{
assert(m_serial != NULL);
// Poll the modem status every 250ms
m_statusTimer.clock(ms);
if (m_statusTimer.hasExpired()) {
@ -474,6 +506,10 @@ void CModem::clock(unsigned int ms)
// if (m_trace)
// CUtils::dump(1U, "GET_STATUS", m_buffer, m_length);
m_p25Space = 0U;
m_nxdnSpace = 0U;
m_pocsagSpace = 0U;
m_tx = (m_buffer[5U] & 0x01U) == 0x01U;
bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U;
@ -493,18 +529,23 @@ void CModem::clock(unsigned int ms)
bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U;
if (dacOverflow)
LogError("MMDVM DAC levels have overflowed");
m_cd = (m_buffer[5U] & 0x40U) == 0x40U;
m_dstarSpace = m_buffer[6U];
m_dmrSpace1 = m_buffer[7U];
m_dmrSpace2 = m_buffer[8U];
m_ysfSpace = m_buffer[9U];
m_p25Space = m_buffer[10U];
m_nxdnSpace = m_buffer[11U];
m_dstarSpace = m_buffer[6U];
m_dmrSpace1 = m_buffer[7U];
m_dmrSpace2 = m_buffer[8U];
m_ysfSpace = m_buffer[9U];
if (m_length > 10U)
m_p25Space = m_buffer[10U];
if (m_length > 11U)
m_nxdnSpace = m_buffer[11U];
if (m_length > 12U)
m_pocsagSpace = m_buffer[12U];
m_inactivityTimer.start();
// LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, int(m_lockout), int(m_cd));
// LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, int(m_lockout), int(m_cd));
}
break;
@ -577,7 +618,7 @@ void CModem::clock(unsigned int ms)
break;
}
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing D-Star data to the MMDVM");
@ -593,7 +634,7 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "TX DMR Data 1", m_buffer, len);
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing DMR data to the MMDVM");
@ -610,7 +651,7 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "TX DMR Data 2", m_buffer, len);
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing DMR data to the MMDVM");
@ -627,7 +668,7 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "TX YSF Data", m_buffer, len);
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing YSF data to the MMDVM");
@ -648,7 +689,7 @@ void CModem::clock(unsigned int ms)
CUtils::dump(1U, "TX P25 LDU", m_buffer, len);
}
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing P25 data to the MMDVM");
@ -665,7 +706,7 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "TX NXDN Data", m_buffer, len);
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing NXDN data to the MMDVM");
@ -674,6 +715,23 @@ void CModem::clock(unsigned int ms)
m_nxdnSpace--;
}
if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) {
unsigned char len = 0U;
m_txPOCSAGData.getData(&len, 1U);
m_txPOCSAGData.getData(m_buffer, len);
if (m_trace)
CUtils::dump(1U, "TX POCSAG Data", m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing POCSAG data to the MMDVM");
m_playoutTimer.start();
m_pocsagSpace--;
}
if (!m_txTransparentData.isEmpty()) {
unsigned char len = 0U;
m_txTransparentData.getData(&len, 1U);
@ -682,7 +740,7 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "TX Transparent Data", m_buffer, len);
int ret = m_serial.write(m_buffer, len);
int ret = m_serial->write(m_buffer, len);
if (ret != int(len))
LogWarning("Error when writing Transparent data to the MMDVM");
}
@ -690,9 +748,11 @@ void CModem::clock(unsigned int ms)
void CModem::close()
{
assert(m_serial != NULL);
::LogMessage("Closing the MMDVM");
m_serial.close();
m_serial->close();
}
unsigned int CModem::readDStarData(unsigned char* data)
@ -993,7 +1053,34 @@ bool CModem::writeNXDNData(const unsigned char* data, unsigned int length)
return true;
}
bool CModem::writeTransparentData(const unsigned char* data, unsigned int length)
bool CModem::hasPOCSAGSpace() const
{
unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U);
return space > 1U;
}
bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
unsigned char buffer[130U];
buffer[0U] = MMDVM_FRAME_START;
buffer[1U] = length + 3U;
buffer[2U] = MMDVM_POCSAG_DATA;
::memcpy(buffer + 3U, data, length);
unsigned char len = length + 3U; // XXX Check lengths
m_txPOCSAGData.addData(&len, 1U);
m_txPOCSAGData.addData(buffer, len);
return true;
}
bool CModem::writeTransparentData(const unsigned char* data, unsigned int length, unsigned int sendFrameType)
{
assert(data != NULL);
assert(length > 0U);
@ -1004,7 +1091,19 @@ bool CModem::writeTransparentData(const unsigned char* data, unsigned int length
buffer[1U] = length + 3U;
buffer[2U] = MMDVM_TRANSPARENT;
::memcpy(buffer + 3U, data, length);
if (sendFrameType>0) {
::memcpy(buffer + 2U, data, length);
length--;
buffer[1U]--;
//when sendFrameType==1 , only 0x80 and 0x90 (MMDVM_SERIAL and MMDVM_TRANSPARENT) are allowed
// and reverted to default (MMDVM_TRANSPARENT) for any other value
//when >1, frame type is not checked
if (sendFrameType==1) {
if ((buffer[2U] & 0xE0) != 0x80) buffer[2U] = MMDVM_TRANSPARENT;
}
} else {
::memcpy(buffer + 3U, data, length);
}
unsigned char len = length + 3U;
m_txTransparentData.addData(&len, 1U);
@ -1015,6 +1114,7 @@ bool CModem::writeTransparentData(const unsigned char* data, unsigned int length
bool CModem::writeSerial(const unsigned char* data, unsigned int length)
{
assert(m_serial != NULL);
assert(data != NULL);
assert(length > 0U);
@ -1026,7 +1126,7 @@ bool CModem::writeSerial(const unsigned char* data, unsigned int length)
::memcpy(buffer + 3U, data, length);
int ret = m_serial.write(buffer, length + 3U);
int ret = m_serial->write(buffer, length + 3U);
return ret != int(length + 3U);
}
@ -1053,6 +1153,8 @@ bool CModem::hasError() const
bool CModem::readVersion()
{
assert(m_serial != NULL);
CThread::sleep(2000U); // 2s
for (unsigned int i = 0U; i < 6U; i++) {
@ -1064,12 +1166,12 @@ bool CModem::readVersion()
// CUtils::dump(1U, "Written", buffer, 3U);
int ret = m_serial.write(buffer, 3U);
int ret = m_serial->write(buffer, 3U);
if (ret != 3)
return false;
#if defined(__APPLE__)
m_serial.setNonblock(true);
m_serial->setNonblock(true);
#endif
for (unsigned int count = 0U; count < MAX_RESPONSES; count++) {
@ -1088,6 +1190,8 @@ bool CModem::readVersion()
m_hwType = HWT_MMDVM_HS_DUAL_HAT;
else if (::memcmp(m_buffer + 4U, "Nano_hotSPOT", 12U) == 0)
m_hwType = HWT_NANO_HOTSPOT;
else if (::memcmp(m_buffer + 4U, "Nano_DV", 7U) == 0)
m_hwType = HWT_NANO_DV;
else if (::memcmp(m_buffer + 4U, "MMDVM_HS-", 9U) == 0)
m_hwType = HWT_MMDVM_HS;
@ -1106,6 +1210,8 @@ bool CModem::readVersion()
bool CModem::readStatus()
{
assert(m_serial != NULL);
unsigned char buffer[3U];
buffer[0U] = MMDVM_FRAME_START;
@ -1114,16 +1220,18 @@ bool CModem::readStatus()
// CUtils::dump(1U, "Written", buffer, 3U);
return m_serial.write(buffer, 3U) == 3;
return m_serial->write(buffer, 3U) == 3;
}
bool CModem::setConfig()
{
unsigned char buffer[20U];
assert(m_serial != NULL);
unsigned char buffer[30U];
buffer[0U] = MMDVM_FRAME_START;
buffer[1U] = 19U;
buffer[1U] = 21U;
buffer[2U] = MMDVM_SET_CONFIG;
@ -1152,6 +1260,8 @@ bool CModem::setConfig()
buffer[4U] |= 0x08U;
if (m_nxdnEnabled)
buffer[4U] |= 0x10U;
if (m_pocsagEnabled)
buffer[4U] |= 0x20U;
buffer[5U] = m_txDelay / 10U; // In 10ms units
@ -1177,10 +1287,14 @@ bool CModem::setConfig()
buffer[18U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F);
// CUtils::dump(1U, "Written", buffer, 19U);
buffer[19U] = (unsigned char)m_ysfTXHang;
int ret = m_serial.write(buffer, 19U);
if (ret != 19)
buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F);
// CUtils::dump(1U, "Written", buffer, 21U);
int ret = m_serial->write(buffer, 21U);
if (ret != 21)
return false;
unsigned int count = 0U;
@ -1212,14 +1326,22 @@ bool CModem::setConfig()
bool CModem::setFrequency()
{
unsigned char buffer[16U];
assert(m_serial != NULL);
unsigned char buffer[20U];
unsigned char len;
if (m_hwType == HWT_DVMEGA)
len = 12U;
else {
buffer[12U] = (unsigned char)(m_rfLevel * 2.55F + 0.5F);
len = 13U;
buffer[13U] = (m_pocsagFrequency >> 0) & 0xFFU;
buffer[14U] = (m_pocsagFrequency >> 8) & 0xFFU;
buffer[15U] = (m_pocsagFrequency >> 16) & 0xFFU;
buffer[16U] = (m_pocsagFrequency >> 24) & 0xFFU;
len = 17U;
}
buffer[0U] = MMDVM_FRAME_START;
@ -1242,7 +1364,7 @@ bool CModem::setFrequency()
// CUtils::dump(1U, "Written", buffer, len);
int ret = m_serial.write(buffer, len);
int ret = m_serial->write(buffer, len);
if (ret != len)
return false;
@ -1273,9 +1395,11 @@ bool CModem::setFrequency()
RESP_TYPE_MMDVM CModem::getResponse()
{
assert(m_serial != NULL);
if (m_offset == 0U) {
// Get the start of the frame or nothing at all
int ret = m_serial.read(m_buffer + 0U, 1U);
int ret = m_serial->read(m_buffer + 0U, 1U);
if (ret < 0) {
LogError("Error when reading from the modem");
return RTM_ERROR;
@ -1292,7 +1416,7 @@ RESP_TYPE_MMDVM CModem::getResponse()
if (m_offset == 1U) {
// Get the length of the frame
int ret = m_serial.read(m_buffer + 1U, 1U);
int ret = m_serial->read(m_buffer + 1U, 1U);
if (ret < 0) {
LogError("Error when reading from the modem");
m_offset = 0U;
@ -1314,7 +1438,7 @@ RESP_TYPE_MMDVM CModem::getResponse()
if (m_offset == 2U) {
// Get the frame type
int ret = m_serial.read(m_buffer + 2U, 1U);
int ret = m_serial->read(m_buffer + 2U, 1U);
if (ret < 0) {
LogError("Error when reading from the modem");
m_offset = 0U;
@ -1330,7 +1454,7 @@ RESP_TYPE_MMDVM CModem::getResponse()
if (m_offset >= 3U) {
// Use later two byte length field
if (m_length == 0U) {
int ret = m_serial.read(m_buffer + 3U, 2U);
int ret = m_serial->read(m_buffer + 3U, 2U);
if (ret < 0) {
LogError("Error when reading from the modem");
m_offset = 0U;
@ -1345,7 +1469,7 @@ RESP_TYPE_MMDVM CModem::getResponse()
}
while (m_offset < m_length) {
int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset);
int ret = m_serial->read(m_buffer + m_offset, m_length - m_offset);
if (ret < 0) {
LogError("Error when reading from the modem");
m_offset = 0U;
@ -1374,6 +1498,8 @@ HW_TYPE CModem::getHWType() const
bool CModem::setMode(unsigned char mode)
{
assert(m_serial != NULL);
unsigned char buffer[4U];
buffer[0U] = MMDVM_FRAME_START;
@ -1383,11 +1509,13 @@ bool CModem::setMode(unsigned char mode)
// CUtils::dump(1U, "Written", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
return m_serial->write(buffer, 4U) == 4;
}
bool CModem::sendCWId(const std::string& callsign)
{
assert(m_serial != NULL);
unsigned int length = callsign.length();
if (length > 200U)
length = 200U;
@ -1403,11 +1531,13 @@ bool CModem::sendCWId(const std::string& callsign)
// CUtils::dump(1U, "Written", buffer, length + 3U);
return m_serial.write(buffer, length + 3U) == int(length + 3U);
return m_serial->write(buffer, length + 3U) == int(length + 3U);
}
bool CModem::writeDMRStart(bool tx)
{
assert(m_serial != NULL);
if (tx && m_tx)
return true;
if (!tx && !m_tx)
@ -1422,11 +1552,13 @@ bool CModem::writeDMRStart(bool tx)
// CUtils::dump(1U, "Written", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
return m_serial->write(buffer, 4U) == 4;
}
bool CModem::writeDMRAbort(unsigned int slotNo)
{
assert(m_serial != NULL);
if (slotNo == 1U)
m_txDMRData1.clear();
else
@ -1441,11 +1573,12 @@ bool CModem::writeDMRAbort(unsigned int slotNo)
// CUtils::dump(1U, "Written", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
return m_serial->write(buffer, 4U) == 4;
}
bool CModem::writeDMRShortLC(const unsigned char* lc)
{
assert(m_serial != NULL);
assert(lc != NULL);
unsigned char buffer[12U];
@ -1465,7 +1598,7 @@ bool CModem::writeDMRShortLC(const unsigned char* lc)
// CUtils::dump(1U, "Written", buffer, 12U);
return m_serial.write(buffer, 12U) == 12;
return m_serial->write(buffer, 12U) == 12;
}
void CModem::printDebug()

21
Modem.h
View file

@ -37,11 +37,12 @@ public:
CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool trace, bool debug);
~CModem();
void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel);
void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled);
void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel);
void setSerialParams(const std::string& protocol, unsigned int address);
void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency);
void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled);
void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel);
void setDMRParams(unsigned int colorCode);
void setYSFParams(bool loDev);
void setYSFParams(bool loDev, unsigned int txHang);
bool open();
@ -61,6 +62,7 @@ public:
bool hasYSFSpace() const;
bool hasP25Space() const;
bool hasNXDNSpace() const;
bool hasPOCSAGSpace() const;
bool hasTX() const;
bool hasCD() const;
@ -74,7 +76,8 @@ public:
bool writeYSFData(const unsigned char* data, unsigned int length);
bool writeP25Data(const unsigned char* data, unsigned int length);
bool writeNXDNData(const unsigned char* data, unsigned int length);
bool writeTransparentData(const unsigned char* data, unsigned int length);
bool writePOCSAGData(const unsigned char* data, unsigned int length);
bool writeTransparentData(const unsigned char* data, unsigned int length, unsigned int sendFrameType);
bool writeDMRStart(bool tx);
bool writeDMRShortLC(const unsigned char* lc);
@ -96,6 +99,7 @@ private:
std::string m_port;
unsigned int m_dmrColorCode;
bool m_ysfLoDev;
unsigned int m_ysfTXHang;
bool m_duplex;
bool m_rxInvert;
bool m_txInvert;
@ -109,19 +113,22 @@ private:
float m_ysfTXLevel;
float m_p25TXLevel;
float m_nxdnTXLevel;
float m_pocsagTXLevel;
float m_rfLevel;
bool m_trace;
bool m_debug;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
unsigned int m_pocsagFrequency;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
bool m_nxdnEnabled;
bool m_pocsagEnabled;
int m_rxDCOffset;
int m_txDCOffset;
CSerialController m_serial;
CSerialController* m_serial;
unsigned char* m_buffer;
unsigned int m_length;
unsigned int m_offset;
@ -137,6 +144,7 @@ private:
CRingBuffer<unsigned char> m_txP25Data;
CRingBuffer<unsigned char> m_rxNXDNData;
CRingBuffer<unsigned char> m_txNXDNData;
CRingBuffer<unsigned char> m_txPOCSAGData;
CRingBuffer<unsigned char> m_rxTransparentData;
CRingBuffer<unsigned char> m_txTransparentData;
CTimer m_statusTimer;
@ -148,6 +156,7 @@ private:
unsigned int m_ysfSpace;
unsigned int m_p25Space;
unsigned int m_nxdnSpace;
unsigned int m_pocsagSpace;
bool m_tx;
bool m_cd;
bool m_lockout;

View file

@ -227,6 +227,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND);
lich.encode(data + 2U);
lich.setDirection(NXDN_LICH_DIRECTION_INBOUND);
netData[0U] = lich.getRaw();
CNXDNSACCH sacch;
@ -245,7 +246,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
scrambler(data + 2U);
writeNetwork(netData, true);
writeNetwork(netData, data[0U] == TAG_EOT ? NNMT_VOICE_TRAILER : NNMT_VOICE_HEADER);
#if defined(DUMP_NXDN)
writeFile(data + 2U);
@ -402,6 +403,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND);
lich.encode(start + 2U);
lich.setDirection(NXDN_LICH_DIRECTION_INBOUND);
netData[0U] = lich.getRaw();
CNXDNSACCH sacch;
@ -424,7 +426,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
scrambler(start + 2U);
writeNetwork(netData, true);
writeNetwork(netData, NNMT_VOICE_HEADER);
#if defined(DUMP_NXDN)
writeFile(start + 2U);
@ -446,6 +448,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND);
lich.encode(data + 2U);
lich.setDirection(NXDN_LICH_DIRECTION_INBOUND);
netData[0U] = lich.getRaw();
// Regenerate SACCH if it's valid
@ -529,7 +532,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne
scrambler(data + 2U);
writeNetwork(netData, false);
writeNetwork(netData, NNMT_VOICE_BODY);
#if defined(DUMP_NXDN)
writeFile(data + 2U);
@ -614,12 +617,15 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data)
lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND);
lich.encode(data + 2U);
lich.setDirection(NXDN_LICH_DIRECTION_INBOUND);
netData[0U] = lich.getRaw();
udch.getRaw(netData + 1U);
unsigned char type = NXDN_MESSAGE_TYPE_DCALL_DATA;
if (validUDCH) {
unsigned char type = layer3.getMessageType();
type = layer3.getMessageType();
data[0U] = type == NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA;
udch.setRAN(m_ran);
@ -631,7 +637,17 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data)
scrambler(data + 2U);
writeNetwork(netData, true);
switch (type) {
case NXDN_MESSAGE_TYPE_DCALL_HDR:
writeNetwork(netData, NNMT_DATA_HEADER);
break;
case NXDN_MESSAGE_TYPE_TX_REL:
writeNetwork(netData, NNMT_DATA_TRAILER);
break;
default:
writeNetwork(netData, NNMT_DATA_BODY);
break;
}
if (m_duplex)
writeQueueRF(data);
@ -1005,7 +1021,7 @@ void CNXDNControl::writeQueueNet(const unsigned char *data)
m_queue.addData(data, len);
}
void CNXDNControl::writeNetwork(const unsigned char *data, bool single)
void CNXDNControl::writeNetwork(const unsigned char *data, NXDN_NETWORK_MESSAGE_TYPE type)
{
assert(data != NULL);
@ -1015,7 +1031,7 @@ void CNXDNControl::writeNetwork(const unsigned char *data, bool single)
if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired())
return;
m_network->write(data, single);
m_network->write(data, type);
}
void CNXDNControl::scrambler(unsigned char* data) const
@ -1043,7 +1059,7 @@ bool CNXDNControl::openFile()
if (m_fp == NULL)
return false;
::fwrite("NXDN", 1U, 3U, m_fp);
::fwrite("NXDN", 1U, 4U, m_fp);
return true;
}

View file

@ -84,7 +84,7 @@ private:
void writeQueueRF(const unsigned char* data);
void writeQueueNet(const unsigned char* data);
void writeNetwork(const unsigned char* data, bool single);
void writeNetwork(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type);
void writeNetwork();
void scrambler(unsigned char* data) const;

0
NXDNConvolution.cpp Executable file → Normal file
View file

View file

@ -89,7 +89,7 @@ bool CNXDNLayer3::getIsGroup() const
unsigned char CNXDNLayer3::getDataBlocks() const
{
return m_data[8U] & 0x0FU;
return (m_data[8U] & 0x0FU) + 1U;
}
void CNXDNLayer3::getData(unsigned char* data) const

View file

@ -56,7 +56,7 @@ bool CNXDNNetwork::open()
return m_socket.open();
}
bool CNXDNNetwork::write(const unsigned char* data, bool single)
bool CNXDNNetwork::write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type)
{
assert(data != NULL);
@ -72,9 +72,28 @@ bool CNXDNNetwork::write(const unsigned char* data, bool single)
buffer[6U] = 0x08U;
buffer[7U] = 0xE0U;
buffer[37U] = 0x23U;
buffer[38U] = single ? 0x1CU : 0x10U;
buffer[39U] = 0x21U;
switch (type) {
case NNMT_VOICE_HEADER:
case NNMT_VOICE_TRAILER:
buffer[37U] = 0x23U;
buffer[38U] = 0x1CU;
buffer[39U] = 0x21U;
break;
case NNMT_VOICE_BODY:
buffer[37U] = 0x23U;
buffer[38U] = 0x10U;
buffer[39U] = 0x21U;
break;
case NNMT_DATA_HEADER:
case NNMT_DATA_BODY:
case NNMT_DATA_TRAILER:
buffer[37U] = 0x23U;
buffer[38U] = 0x02U;
buffer[39U] = 0x18U;
break;
default:
return false;
}
::memcpy(buffer + 40U, data, 33U);

View file

@ -27,6 +27,15 @@
#include <cstdint>
#include <string>
enum NXDN_NETWORK_MESSAGE_TYPE {
NNMT_VOICE_HEADER,
NNMT_VOICE_BODY,
NNMT_VOICE_TRAILER,
NNMT_DATA_HEADER,
NNMT_DATA_BODY,
NNMT_DATA_TRAILER
};
class CNXDNNetwork {
public:
CNXDNNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug);
@ -36,7 +45,7 @@ public:
void enable(bool enabled);
bool write(const unsigned char* data, bool single);
bool write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type);
bool read(unsigned char* data);

0
NXDNSACCH.cpp Executable file → Normal file
View file

View file

@ -187,8 +187,6 @@ void CNXDNUDCH::getRaw(unsigned char* data) const
::memset(data, 0x00U, 25U);
::memcpy(data, m_data, 23U);
CNXDNCRC::encodeCRC15(data, 184U);
}
void CNXDNUDCH::setRAN(unsigned char ran)

View file

@ -99,7 +99,7 @@ void CNextion::setIdleInt()
sendCommand("page MMDVM");
sendCommandAction(1U);
char command[30U];
char command[100U];
::sprintf(command, "dim=%u", m_idleBrightness);
sendCommand(command);
@ -165,6 +165,27 @@ void CNextion::setLockoutInt()
m_mode = MODE_LOCKOUT;
}
void CNextion::setQuitInt()
{
sendCommand("page MMDVM");
sendCommandAction(1U);
char command[100];
::sprintf(command, "dim=%u", m_idleBrightness);
sendCommand(command);
::sprintf(command, "t3.txt=\"%s\"", m_ipaddress.c_str());
sendCommand(command);
sendCommandAction(16U);
sendCommand("t0.txt=\"MMDVM STOPPED\"");
sendCommandAction(19U);
m_clockDisplayTimer.stop();
m_mode = MODE_QUIT;
}
void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
@ -178,7 +199,7 @@ void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your,
sendCommandAction(2U);
}
char text[30U];
char text[50U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
@ -273,7 +294,7 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
}
}
char text[30U];
char text[50U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
@ -367,7 +388,7 @@ void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, co
}
if (slotNo == 1U) {
char text[40U];
char text[50U];
::sprintf(text, "t0.txt=\"1 %s %s\"", type, talkerAlias);
if (m_screenLayout == 2U) {
@ -384,7 +405,7 @@ void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, co
sendCommand(text);
sendCommandAction(63U);
} else {
char text[40U];
char text[50U];
::sprintf(text, "t2.txt=\"2 %s %s\"", type, talkerAlias);
if (m_screenLayout == 2U) {
@ -679,6 +700,37 @@ void CNextion::clearNXDNInt()
sendCommand("t3.txt=\"\"");
}
void CNextion::writePOCSAGInt(uint32_t ric, const std::string& message)
{
if (m_mode != MODE_POCSAG) {
sendCommand("page POCSAG");
sendCommandAction(7U);
}
char text[200U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
::sprintf(text, "t0.txt=\"RIC: %u\"", ric);
sendCommand(text);
sendCommandAction(132U);
::sprintf(text, "t1.txt=\"%s\"", message.c_str());
sendCommand(text);
sendCommandAction(133U);
m_clockDisplayTimer.stop();
m_mode = MODE_POCSAG;
}
void CNextion::clearPOCSAGInt()
{
sendCommand("t0.txt=\"Waiting\"");
sendCommandAction(134U);
sendCommand("t1.txt=\"\"");
}
void CNextion::writeCWInt()
{
sendCommand("t1.txt=\"Sending CW Ident\"");
@ -719,10 +771,6 @@ void CNextion::clockInt(unsigned int ms)
void CNextion::close()
{
sendCommand("page MMDVM");
sendCommandAction(1U);
sendCommand("t1.txt=\"MMDVM STOPPED\"");
sendCommandAction(19U);
m_serial->close();
delete m_serial;
}

View file

@ -40,6 +40,7 @@ protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
@ -68,6 +69,9 @@ protected:
virtual void writeNXDNBERInt(float ber);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -15,8 +15,8 @@ are activated.
0 = auto (future use, for now it's G4KLX layout)
1 = G4KLX layout
2 = ON7LDS layout (see README-L2)
3 = ON7LDS DIY layout (this README file)
4 = ON7LDS DIY layout High Speed (this README file)
3 = ON7LDS DIY layout (this README file)
4 = ON7LDS DIY layout High Speed (this README file)
screenLayout 3 and 4 are the same, but selecting 3, MMDVMHost will communicate
at 9600bps with the display and selecting 4 will set the baudrate to 115200.
@ -69,7 +69,7 @@ Check the NextionDriver program and the display layouts there to see
NOTE: it might be good to *not* remove fields from the display when you do not
need them, but make them small (i.e. 10 x 10 pixels) and put them aside:
Give them the same font color and background color as where you put them
Give them the same font colour and background colour as where you put them
OR
Put them behind some picture: put them on top of the picture, then select the
picture and click the 'Bring Top' button to put it on top of all those fields
@ -94,6 +94,7 @@ changed field.
4 : page YSF
5 : page P25
6 : page NXDN
7 : page POCSAG
11 : IDLE
12 : CW
@ -147,6 +148,9 @@ changed field.
124 : RSSI
125 : ber
132 : RIC
133 : message text
134 : waiting
Fields (and their numbers) on the pages, used by MMDVMHost
@ -154,7 +158,7 @@ Fields (and their numbers) on the pages, used by MMDVMHost
MMDVM
t0 : owner call & ID / errortext LOCKOUT
t1 : status / ERROR
t1 : status / ERROR
t2 : date & time
screenLayout >1 :
@ -211,3 +215,6 @@ t1 : dst
t2 : rssi
t3 : ber
POCSAG
t0 : waiting / RIC
t1 : message

View file

@ -16,8 +16,8 @@ Layout 2 is a no-nonsense layout. It is the original (G4KLX) layout with the Tal
Layout 3 (as is 4) is a layout without any predefined layout options (color, fonts). It sends the fields **and** information about what was sent to the display, so all layout processing can and should be done in the display itself.
More information about the layouts can be found in
* README-L2 for the screenLayout 2 setting
* README-L2 for the screenLayout 3 and 4 settings
* README-L2 for the screenLayout 2 setting (list of field names : see README-L3)
* README-L3 for the screenLayout 3 and 4 settings
When you want extra control over what has to be sent to the Nextion display, you could consider the program 'NextionDriver' at https://github.com/on7lds/NextionDriver as a companion to MMDVMHost.

View file

@ -56,6 +56,10 @@ void CNullDisplay::setLockoutInt()
{
}
void CNullDisplay::setQuitInt()
{
}
void CNullDisplay::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
#if defined(RASPBERRY_PI)
@ -126,6 +130,20 @@ void CNullDisplay::clearNXDNInt()
#endif
}
void CNullDisplay::writePOCSAGInt(uint32_t ric, const std::string& message)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearPOCSAGInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeCWInt()
{
}

View file

@ -37,6 +37,7 @@ protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
@ -53,6 +54,9 @@ protected:
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

363
OLED.cpp
View file

@ -106,45 +106,66 @@ const unsigned char logo_fusion_bmp [] =
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
//Logo P25 128x16px
const unsigned char logo_P25_bmp [] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf8, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f,
0xf8, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f,
0xf8, 0x00, 0xe0, 0x00, 0x00, 0xff, 0x00, 0x07, 0xc0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xff,
0xf0, 0x01, 0xff, 0xfc, 0x00, 0x7e, 0x00, 0x3f, 0xf8, 0x00, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff,
0xf0, 0x01, 0xff, 0xfe, 0x00, 0x7c, 0x00, 0x7f, 0xfc, 0x00, 0x7f, 0xe0, 0x0f, 0xff, 0xff, 0xff,
0xf0, 0x01, 0xff, 0xfe, 0x00, 0x7e, 0x00, 0x7f, 0xf8, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff,
0xf0, 0x01, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x1f,
0xf0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0x9f, 0xff, 0x80, 0x07,
0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03,
0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03,
0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xc0, 0x03,
0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x0f, 0xfe, 0x00, 0x0f,
0xf0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x3f,
0xf0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x03, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
//Logo P25 128x16px
const unsigned char logo_P25_bmp [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00,
0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00,
0x01, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xf8, 0x3f, 0xff, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00,
0x01, 0xff, 0xc0, 0x00, 0x7f, 0xf1, 0xff, 0xc0, 0x07, 0xff, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0xc0, 0x00, 0x3f, 0xf3, 0xff, 0x80, 0x03, 0xff, 0x81, 0xff, 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0xc0, 0x00, 0x3f, 0xf1, 0xff, 0x80, 0x07, 0xff, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00,
0x01, 0xff, 0xc0, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00,
0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0x80,
0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0,
0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0,
0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x03, 0xf0, 0x00, 0x03, 0xff, 0xc0,
0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x00, 0x1f, 0xff, 0x00,
0x01, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xfc, 0x00,
0x01, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Logo NXDN_sm, 128x16px
const unsigned char logo_NXDN_bmp [] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf0, 0x1f, 0xf8, 0x0f, 0x00, 0xff, 0x80, 0x7c, 0x00, 0x0f, 0xff, 0x80, 0x7f, 0xe0, 0x7f,
0xff, 0xe0, 0x0f, 0xf0, 0x1f, 0x80, 0x7e, 0x01, 0xf8, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0xc0, 0x7f,
0xff, 0xc0, 0x07, 0xe0, 0x3f, 0x80, 0x38, 0x07, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x3f, 0x80, 0xff,
0xff, 0x80, 0x03, 0xc0, 0x3f, 0xc0, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x3e, 0x00, 0x1f, 0x01, 0xff,
0xff, 0x00, 0x03, 0x80, 0x7f, 0xe0, 0x00, 0xff, 0xc0, 0x3f, 0x80, 0x3c, 0x00, 0x0e, 0x03, 0xff,
0xfe, 0x00, 0x01, 0x00, 0xff, 0xe0, 0x03, 0xff, 0x80, 0x7f, 0x80, 0x78, 0x08, 0x04, 0x03, 0xff,
0xfc, 0x03, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0x00, 0xff, 0x00, 0xf0, 0x1c, 0x00, 0x07, 0xff,
0xfc, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x01, 0xfe, 0x01, 0xfc, 0x01, 0xe0, 0x1e, 0x00, 0x0f, 0xff,
0xf8, 0x0f, 0xc0, 0x07, 0xf0, 0x0e, 0x00, 0xfc, 0x00, 0x00, 0x07, 0xc0, 0x3f, 0x00, 0x1f, 0xff,
0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x3f, 0xff,
0xe0, 0x3f, 0xf0, 0x0e, 0x01, 0xff, 0x80, 0x38, 0x00, 0x07, 0xff, 0x80, 0xff, 0x80, 0x7f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf0, 0x1f, 0xf8, 0x0f, 0x00, 0xff, 0x80, 0x7c, 0x00, 0x0f, 0xff, 0x80, 0x7f, 0xe0, 0x7f,
0xff, 0xe0, 0x0f, 0xf0, 0x1f, 0x80, 0x7e, 0x01, 0xf8, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0xc0, 0x7f,
0xff, 0xc0, 0x07, 0xe0, 0x3f, 0x80, 0x38, 0x07, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x3f, 0x80, 0xff,
0xff, 0x80, 0x03, 0xc0, 0x3f, 0xc0, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x3e, 0x00, 0x1f, 0x01, 0xff,
0xff, 0x00, 0x03, 0x80, 0x7f, 0xe0, 0x00, 0xff, 0xc0, 0x3f, 0x80, 0x3c, 0x00, 0x0e, 0x03, 0xff,
0xfe, 0x00, 0x01, 0x00, 0xff, 0xe0, 0x03, 0xff, 0x80, 0x7f, 0x80, 0x78, 0x08, 0x04, 0x03, 0xff,
0xfc, 0x03, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0x00, 0xff, 0x00, 0xf0, 0x1c, 0x00, 0x07, 0xff,
0xfc, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x01, 0xfe, 0x01, 0xfc, 0x01, 0xe0, 0x1e, 0x00, 0x0f, 0xff,
0xf8, 0x0f, 0xc0, 0x07, 0xf0, 0x0e, 0x00, 0xfc, 0x00, 0x00, 0x07, 0xc0, 0x3f, 0x00, 0x1f, 0xff,
0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x3f, 0xff,
0xe0, 0x3f, 0xf0, 0x0e, 0x01, 0xff, 0x80, 0x38, 0x00, 0x07, 0xff, 0x80, 0xff, 0x80, 0x7f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// Logo POCASG/DAPNET, 128x16px
const unsigned char logo_POCSAG_bmp [] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf8, 0x7f, 0xfe, 0x03, 0xfe, 0xfe, 0x03, 0xdf, 0xf6, 0x00, 0x00, 0x1f, 0xff,
0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xfc, 0xfc, 0xfe, 0xfc, 0xcf, 0xf6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xfe, 0x7d, 0x7e, 0xfe, 0xc7, 0xf6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfb, 0x7a, 0x7e, 0xff, 0x79, 0x7e, 0xfe, 0xd3, 0xf6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xff, 0x7b, 0xbe, 0xfe, 0xdb, 0xf6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xff, 0xbb, 0xbe, 0xfe, 0xdd, 0xf6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf9, 0xff, 0xbe, 0xff, 0xb7, 0xde, 0xfe, 0xde, 0xf6, 0x01, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xee, 0x77, 0xbe, 0xff, 0xb7, 0xde, 0x81, 0xde, 0x76, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xdf, 0xb7, 0x7e, 0xff, 0xa0, 0x1e, 0xff, 0xdf, 0x36, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xdf, 0xbc, 0xfe, 0xff, 0x6f, 0xee, 0xff, 0xdf, 0xb6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xdf, 0xbf, 0xfe, 0xff, 0x6f, 0xee, 0xff, 0xdf, 0xd6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xdf, 0xbf, 0xfe, 0xfe, 0xdf, 0xf6, 0xff, 0xdf, 0xe6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xdf, 0x7f, 0xfe, 0xf9, 0xdf, 0xf6, 0xff, 0xdf, 0xe6, 0xff, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xe6, 0x7f, 0xfe, 0x07, 0xff, 0xf6, 0xff, 0xdf, 0xf6, 0x00, 0xfb, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
@ -176,10 +197,10 @@ bool COLED::open()
{
// I2C change parameters to fit to your LCD
if ( !display.init(OLED_I2C_RESET, m_displayType) )
return false;
return false;
}
display.begin();
display.invertDisplay(m_displayInvert ? 1 : 0);
@ -200,7 +221,7 @@ bool COLED::open()
void COLED::setIdleInt()
{
m_mode = MODE_IDLE;
m_mode = MODE_IDLE;
display.clearDisplay();
OLED_statusbar();
@ -212,26 +233,26 @@ void COLED::setIdleInt()
// display.setTextSize(1);
display.startscrolldiagright(0x00,0x0f); //the MMDVM logo scrolls the whole screen
display.display();
unsigned char info[100U];
CNetworkInfo* m_network;
passCounter ++;
if (passCounter > 253U)
networkInfoInitialized = false;
if (! networkInfoInitialized) {
//LogMessage("Initialize CNetworkInfo");
info[0]=0;
m_network = new CNetworkInfo;
m_network->getNetworkInterface(info);
m_ipaddress = (char*)info;
delete m_network;
networkInfoInitialized = true;
passCounter = 0;
}
unsigned char info[100U];
CNetworkInfo* m_network;
passCounter ++;
if (passCounter > 253U)
networkInfoInitialized = false;
if (! networkInfoInitialized) {
//LogMessage("Initialize CNetworkInfo");
info[0]=0;
m_network = new CNetworkInfo;
m_network->getNetworkInterface(info);
m_ipaddress = (char*)info;
delete m_network;
networkInfoInitialized = true;
passCounter = 0;
}
}
void COLED::setErrorInt(const char* text)
@ -262,6 +283,21 @@ void COLED::setLockoutInt()
display.display();
}
void COLED::setQuitInt()
{
m_mode = MODE_QUIT;
display.clearDisplay();
OLED_statusbar();
display.setCursor(0,30);
display.setTextSize(3);
display.print("Stopped");
display.setTextSize(1);
display.display();
}
void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
m_mode = MODE_DSTAR;
@ -277,9 +313,9 @@ void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, co
display.setCursor(0,OLED_LINE5);
display.printf("via %.8s",reflector);
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
OLED_statusbar();
display.display();
@ -291,9 +327,9 @@ void COLED::clearDStarInt()
display.setCursor(40,OLED_LINE3);
display.print("Listening");
display.setCursor(0,OLED_LINE5);
display.printf("%s",m_ipaddress.c_str());
display.setCursor(0,OLED_LINE5);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
@ -302,77 +338,74 @@ void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,co
{
if (m_mode != MODE_DMR) {
display.clearDisplay();
m_mode = MODE_DMR;
clearDMRInt(slotNo);
}
// if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2
// if single slot, use lines 3-4
if ( m_slot1Enabled && m_slot2Enabled ) {
display.clearDisplay();
m_mode = MODE_DMR;
clearDMRInt(slotNo);
}
// if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2
// if single slot, use lines 3-4
if ( m_slot1Enabled && m_slot2Enabled ) {
if (slotNo == 1U) {
display.fillRect(0,OLED_LINE2,display.width(),40,BLACK);
display.setCursor(0,OLED_LINE2);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE3);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
else
{
display.fillRect(0,OLED_LINE4,display.width(),40,BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE5);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
if (slotNo == 1U) {
display.fillRect(0,OLED_LINE2,display.width(),40,BLACK);
display.setCursor(0,OLED_LINE2);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE3);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
else
{
display.fillRect(0,OLED_LINE4,display.width(),40,BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE5);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
}
else
{
display.fillRect(0,OLED_LINE3,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE3);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE4);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
}
else
{
display.fillRect(0,OLED_LINE3,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE3);
display.printf("%s",src.c_str());
display.setCursor(0,OLED_LINE4);
display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str());
}
display.fillRect(0,OLED_LINE6,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.fillRect(0,OLED_LINE6,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
OLED_statusbar();
display.display();
}
}
void COLED::clearDMRInt(unsigned int slotNo)
{
// if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2
// if single slot, use lines 3-4
if ( m_slot1Enabled && m_slot2Enabled ){
if (slotNo == 1U)
{
display.fillRect(0, OLED_LINE3, display.width(), 40, BLACK);
display.setCursor(0,OLED_LINE3);
display.print("Slot: 1 Listening");
}
else
{
display.fillRect(0, OLED_LINE5, display.width(), 40, BLACK);
display.setCursor(0, OLED_LINE5);
display.print("Slot: 2 Listening");
}
}
else {
display.fillRect(0, OLED_LINE4, display.width(), 40, BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("Slot: %i Listening",slotNo);
}
// if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2
// if single slot, use lines 3-4
if ( m_slot1Enabled && m_slot2Enabled ){
if (slotNo == 1U) {
display.fillRect(0, OLED_LINE3, display.width(), 40, BLACK);
display.setCursor(0,OLED_LINE3);
display.print("Slot: 1 Listening");
}
else {
display.fillRect(0, OLED_LINE5, display.width(), 40, BLACK);
display.setCursor(0, OLED_LINE5);
display.print("Slot: 2 Listening");
}
}
else {
display.fillRect(0, OLED_LINE4, display.width(), 40, BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("Slot: %i Listening",slotNo);
}
display.fillRect(0, OLED_LINE6, display.width(), 20, BLACK);
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.fillRect(0, OLED_LINE6, display.width(), 20, BLACK);
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
@ -400,9 +433,9 @@ void COLED::clearFusionInt()
display.setCursor(40,OLED_LINE4);
display.print("Listening");
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
@ -412,7 +445,7 @@ void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const
m_mode = MODE_P25;
display.clearDisplay();
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK);
display.setCursor(0,OLED_LINE3);
display.printf("%s %.10s", type, source);
@ -426,13 +459,13 @@ void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const
void COLED::clearP25Int()
{
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK);
display.setCursor(40,OLED_LINE4);
display.print("Listening");
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
@ -442,7 +475,7 @@ void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, cons
m_mode = MODE_NXDN;
display.clearDisplay();
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK);
display.setCursor(0,OLED_LINE3);
display.printf("%s %.10s", type, source);
@ -455,14 +488,44 @@ void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, cons
}
void COLED::clearNXDNInt()
{
display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK);
display.setCursor(40,OLED_LINE4);
display.print("Listening");
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
void COLED::writePOCSAGInt(uint32_t ric, const std::string& message)
{
m_mode = MODE_POCSAG;
display.clearDisplay();
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.setCursor(0,OLED_LINE3);
display.printf("RIC: %u", ric);
display.setCursor(0,OLED_LINE5);
display.printf("MSG: %s", message.c_str());
OLED_statusbar();
display.display();
}
void COLED::clearPOCSAGInt()
{
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.setCursor(40,OLED_LINE4);
display.print("Listening");
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.setCursor(0,OLED_LINE6);
display.printf("%s",m_ipaddress.c_str());
display.display();
}
@ -496,13 +559,13 @@ void COLED::clearCWInt()
void COLED::close()
{
display.clearDisplay();
display.fillRect(0, 0, display.width(), 16, BLACK);
display.startscrollright(0x00,0x01);
display.fillRect(0, 0, display.width(), 16, BLACK);
display.startscrollright(0x00,0x01);
display.setCursor(0,00);
display.setTextSize(2);
display.print("-CLOSE-");
display.display();
display.close();
}
@ -514,18 +577,20 @@ void COLED::OLED_statusbar()
display.setCursor(0,0);
if (m_mode == MODE_DMR)
display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE);
else if (m_mode == MODE_DSTAR)
display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE);
else if (m_mode == MODE_YSF)
display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE);
else if (m_mode == MODE_P25)
display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE);
else if (m_mode == MODE_NXDN)
display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE);
else if (m_mode == MODE_POCSAG)
display.drawBitmap(0, 0, logo_POCSAG_bmp, 128, 16, WHITE);
else
display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE);
display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE);
if (m_displayScroll)
display.startscrollright(0x00,0x02);
display.startscrollright(0x00,0x02);
}

5
OLED.h
View file

@ -49,6 +49,8 @@ public:
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
@ -65,6 +67,9 @@ public:
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -176,6 +177,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
case P25_DUID_PDU:
duid = P25_DUID_PDU;
break;
case P25_DUID_TSDU:
duid = P25_DUID_TSDU;
break;
default:
break;
}
@ -346,6 +350,85 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
return true;
}
} else if (duid == P25_DUID_TSDU) {
if (m_rfState != RS_RF_DATA) {
m_rfPDUCount = 0U;
m_rfPDUBits = 0U;
m_rfState = RS_RF_DATA;
m_rfDataFrames = 0U;
}
bool ret = m_rfData.decodeTSDU(data + 2U);
if (!ret) {
m_lastDUID = duid;
return false;
}
unsigned int srcId = m_rfData.getSrcId();
unsigned int dstId = m_rfData.getDstId();
unsigned char data[P25_TSDU_FRAME_LENGTH_BYTES + 2U];
switch (m_rfData.getLCF()) {
case P25_LCF_TSBK_CALL_ALERT:
LogMessage("P25, received RF TSDU transmission, CALL ALERT from %u to %u", srcId, dstId);
::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES);
// Regenerate Sync
CSync::addP25Sync(data + 2U);
// Regenerate NID
m_nid.encode(data + 2U, P25_DUID_TSDU);
// Regenerate TDULC Data
m_rfData.encodeTSDU(data + 2U);
// Add busy bits
addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false);
// Set first busy bits to 1,1
setBusyBits(data + 2U, P25_SS0_START, true, true);
if (m_duplex) {
data[0U] = TAG_DATA;
data[1U] = 0x00U;
writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U);
}
break;
case P25_LCF_TSBK_ACK_RSP_FNE:
LogMessage("P25, received RF TSDU transmission, ACK RESPONSE FNE from %u to %u", srcId, dstId);
::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES);
// Regenerate Sync
CSync::addP25Sync(data + 2U);
// Regenerate NID
m_nid.encode(data + 2U, P25_DUID_TSDU);
// Regenerate TDULC Data
m_rfData.encodeTSDU(data + 2U);
// Add busy bits
addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false);
// Set first busy bits to 1,1
setBusyBits(data + 2U, P25_SS0_START, true, true);
if (m_duplex) {
data[0U] = TAG_DATA;
data[1U] = 0x00U;
writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U);
}
break;
default:
LogMessage("P25, recieved RF TSDU transmission, unhandled LCF $%02X", m_rfData.getLCF());
break;
}
m_rfState = RS_RF_LISTENING;
return true;
} else if (duid == P25_DUID_TERM || duid == P25_DUID_TERM_LC) {
if (m_rfState == RS_RF_AUDIO) {
writeNetwork(m_rfLDU, m_lastDUID, true);
@ -512,6 +595,10 @@ unsigned int CP25Control::readModem(unsigned char* data)
void CP25Control::writeNetwork()
{
unsigned char data[100U];
if (m_network == NULL)
return;
unsigned int length = m_network->read(data, 100U);
if (length == 0U)
return;
@ -693,6 +780,14 @@ void CP25Control::writeNetwork(const unsigned char *data, unsigned char type, bo
}
}
void CP25Control::setBusyBits(unsigned char* data, unsigned int ssOffset, bool b1, bool b2)
{
assert(data != NULL);
WRITE_BIT(data, ssOffset, b1);
WRITE_BIT(data, ssOffset + 1U, b2);
}
void CP25Control::addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2)
{
assert(data != NULL);

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -94,6 +95,7 @@ private:
void writeNetwork(const unsigned char *data, unsigned char type, bool end);
void writeNetwork();
void setBusyBits(unsigned char* data, unsigned int ssOffset, bool b1, bool b2);
void addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2);
void checkNetLDU1();

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -19,6 +20,7 @@
#include "P25Data.h"
#include "P25Defines.h"
#include "P25Utils.h"
#include "CRC.h"
#include "Hamming.h"
#include "Utils.h"
#include "Log.h"
@ -54,7 +56,8 @@ m_lcf(0x00U),
m_emergency(false),
m_srcId(0U),
m_dstId(0U),
m_rs241213()
m_rs241213(),
m_trellis()
{
m_mi = new unsigned char[P25_MI_LENGTH_BYTES];
}
@ -206,6 +209,116 @@ void CP25Data::encodeLDU2(unsigned char* data)
CP25Utils::encode(raw, data, 1356U, 1398U);
}
bool CP25Data::decodeTSDU(const unsigned char* data)
{
assert(data != NULL);
// deinterleave
unsigned char tsbk[12U];
unsigned char raw[25U];
CP25Utils::decode(data, raw, 114U, 318U);
// decode 1/2 rate Trellis & check CRC-CCITT 16
try {
bool ret = m_trellis.decode12(raw, tsbk);
if (ret)
ret = CCRC::checkCCITT162(tsbk, 12U);
if (!ret)
return false;
}
catch (...) {
CUtils::dump(2U, "P25, CRC failed with input data", tsbk, 12U);
return false;
}
m_lcf = tsbk[0U] & 0x3F;
m_mfId = tsbk[1U];
unsigned long long tsbkValue = 0U;
// combine bytes into rs value
tsbkValue = tsbk[2U];
tsbkValue = (tsbkValue << 8) + tsbk[3U];
tsbkValue = (tsbkValue << 8) + tsbk[4U];
tsbkValue = (tsbkValue << 8) + tsbk[5U];
tsbkValue = (tsbkValue << 8) + tsbk[6U];
tsbkValue = (tsbkValue << 8) + tsbk[7U];
tsbkValue = (tsbkValue << 8) + tsbk[8U];
tsbkValue = (tsbkValue << 8) + tsbk[9U];
switch (m_lcf) {
case P25_LCF_TSBK_CALL_ALERT:
m_dstId = (unsigned int)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address
m_srcId = (unsigned int)(tsbkValue & 0xFFFFFFU); // Source Radio Address
break;
case P25_LCF_TSBK_ACK_RSP_FNE:
m_serviceType = (unsigned char)((tsbkValue >> 56) & 0xFFU); // Service Type
m_dstId = (unsigned int)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address
m_srcId = (unsigned int)(tsbkValue & 0xFFFFFFU); // Source Radio Address
break;
default:
LogMessage("P25, unknown LCF value in TSDU - $%02X", m_lcf);
break;
}
return true;
}
void CP25Data::encodeTSDU(unsigned char* data)
{
assert(data != NULL);
unsigned char tsbk[12U];
::memset(tsbk, 0x00U, 12U);
unsigned long long tsbkValue = 0U;
tsbk[0U] = m_lcf;
tsbk[0U] |= 0x80;
tsbk[1U] = m_mfId;
switch (m_lcf) {
case P25_LCF_TSBK_CALL_ALERT:
tsbkValue = 0U;
tsbkValue = (tsbkValue << 16) + 0U;
tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address
tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address
break;
case P25_LCF_TSBK_ACK_RSP_FNE:
tsbkValue = 0U; // Additional Info. Flag
tsbkValue = (tsbkValue << 1) + 0U; // Extended Address Flag
tsbkValue = (tsbkValue << 16) + (m_serviceType & 0xFF); // Service Type
tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address
tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address
break;
default:
LogMessage("P25, unknown LCF value in TSDU - $%02X", m_lcf);
break;
}
// split rs value into bytes
tsbk[2U] = (unsigned char)((tsbkValue >> 56) & 0xFFU);
tsbk[3U] = (unsigned char)((tsbkValue >> 48) & 0xFFU);
tsbk[4U] = (unsigned char)((tsbkValue >> 40) & 0xFFU);
tsbk[5U] = (unsigned char)((tsbkValue >> 32) & 0xFFU);
tsbk[6U] = (unsigned char)((tsbkValue >> 24) & 0xFFU);
tsbk[7U] = (unsigned char)((tsbkValue >> 16) & 0xFFU);
tsbk[8U] = (unsigned char)((tsbkValue >> 8) & 0xFFU);
tsbk[9U] = (unsigned char)((tsbkValue >> 0) & 0xFFU);
// compute CRC-CCITT 16
CCRC::addCCITT162(tsbk, 12U);
unsigned char raw[25U];
::memset(raw, 0x00U, 25U);
// encode 1/2 rate Trellis
m_trellis.encode12(tsbk, raw);
// interleave
CP25Utils::encode(raw, data, 114U, 318U);
}
void CP25Data::setMI(const unsigned char* mi)
{
assert(mi != NULL);
@ -290,6 +403,16 @@ unsigned int CP25Data::getDstId() const
return m_dstId;
}
void CP25Data::setServiceType(unsigned char type)
{
m_serviceType = type;
}
unsigned char CP25Data::getServiceType() const
{
return m_serviceType;
}
void CP25Data::reset()
{
::memset(m_mi, 0x00U, P25_MI_LENGTH_BYTES);

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -20,6 +21,7 @@
#define P25Data_H
#include "RS241213.h"
#include "P25Trellis.h"
class CP25Data {
public:
@ -33,6 +35,9 @@ public:
void encodeLDU2(unsigned char* data);
bool decodeTSDU(const unsigned char* data);
void encodeTSDU(unsigned char* data);
void setMI(const unsigned char* mi);
void getMI(unsigned char* mi) const;
@ -57,6 +62,9 @@ public:
void setDstId(unsigned int id);
unsigned int getDstId() const;
void setServiceType(unsigned char type);
unsigned char getServiceType() const;
void reset();
private:
@ -68,7 +76,9 @@ private:
bool m_emergency;
unsigned int m_srcId;
unsigned int m_dstId;
unsigned char m_serviceType;
CRS241213 m_rs241213;
CP25Trellis m_trellis;
void decodeLDUHamming(const unsigned char* raw, unsigned char* data);
void encodeLDUHamming(unsigned char* data, const unsigned char* raw);

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -31,6 +32,9 @@ const unsigned int P25_TERM_FRAME_LENGTH_BITS = P25_TERM_FRAME_LENGTH_BYTES * 8
const unsigned int P25_TERMLC_FRAME_LENGTH_BYTES = 54U;
const unsigned int P25_TERMLC_FRAME_LENGTH_BITS = P25_TERMLC_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_TSDU_FRAME_LENGTH_BYTES = 45U;
const unsigned int P25_TSDU_FRAME_LENGTH_BITS = P25_TSDU_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_SYNC_LENGTH_BYTES = 6U;
const unsigned int P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U;
@ -54,6 +58,9 @@ const unsigned int P25_MI_LENGTH_BYTES = 9U;
const unsigned char P25_LCF_GROUP = 0x00U;
const unsigned char P25_LCF_PRIVATE = 0x03U;
const unsigned char P25_LCF_TSBK_CALL_ALERT = 0x1FU;
const unsigned char P25_LCF_TSBK_ACK_RSP_FNE = 0x20U;
const unsigned int P25_SS0_START = 70U;
const unsigned int P25_SS1_START = 71U;
const unsigned int P25_SS_INCREMENT = 72U;
@ -61,6 +68,7 @@ const unsigned int P25_SS_INCREMENT = 72U;
const unsigned char P25_DUID_HEADER = 0x00U;
const unsigned char P25_DUID_TERM = 0x03U;
const unsigned char P25_DUID_LDU1 = 0x05U;
const unsigned char P25_DUID_TSDU = 0x07U;
const unsigned char P25_DUID_LDU2 = 0x0AU;
const unsigned char P25_DUID_PDU = 0x0CU;
const unsigned char P25_DUID_TERM_LC = 0x0FU;

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -33,6 +34,7 @@ m_ldu1(NULL),
m_ldu2(NULL),
m_termlc(NULL),
m_term(NULL),
m_tsdu(NULL),
m_pdu(NULL)
{
CBCH bch;
@ -72,6 +74,13 @@ m_pdu(NULL)
bch.encode(m_term);
m_term[7U] &= 0xFEU; // Clear the parity bit
m_tsdu = new unsigned char[P25_NID_LENGTH_BYTES];
m_tsdu[0U] = (nac >> 4) & 0xFFU;
m_tsdu[1U] = (nac << 4) & 0xF0U;
m_tsdu[1U] |= P25_DUID_TSDU;
bch.encode(m_tsdu);
m_tsdu[7U] &= 0xFEU; // Clear the parity bit
m_pdu = new unsigned char[P25_NID_LENGTH_BYTES];
m_pdu[0U] = (nac >> 4) & 0xFFU;
m_pdu[1U] = (nac << 4) & 0xF0U;
@ -87,6 +96,7 @@ CP25NID::~CP25NID()
delete[] m_ldu2;
delete[] m_termlc;
delete[] m_term;
delete[] m_tsdu;
delete[] m_pdu;
}
@ -127,6 +137,12 @@ bool CP25NID::decode(const unsigned char* data)
return true;
}
errs = CP25Utils::compare(nid, m_tsdu, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_TSDU;
return true;
}
errs = CP25Utils::compare(nid, m_pdu, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_PDU;
@ -156,6 +172,9 @@ void CP25NID::encode(unsigned char* data, unsigned char duid) const
case P25_DUID_TERM_LC:
CP25Utils::encode(m_termlc, data, 48U, 114U);
break;
case P25_DUID_TSDU:
CP25Utils::encode(m_tsdu, data, 48U, 114U);
break;
case P25_DUID_PDU:
CP25Utils::encode(m_pdu, data, 48U, 114U);
break;

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Bryan Biedenkapp <gatekeep@gmail.com>
*
* 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
@ -37,6 +38,7 @@ private:
unsigned char* m_ldu2;
unsigned char* m_termlc;
unsigned char* m_term;
unsigned char* m_tsdu;
unsigned char* m_pdu;
};

416
POCSAGControl.cpp Normal file
View file

@ -0,0 +1,416 @@
/*
* Copyright (C) 2018 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 "POCSAGControl.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <ctime>
// #define DUMP_POCSAG
const struct BCD {
char m_c;
uint32_t m_bcd[5U];
} BCD_VALUES[] = {
{ '0', {0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}},
{ '1', {0x40000000U, 0x04000000U, 0x00400000U, 0x00040000U, 0x00004000U}},
{ '2', {0x20000000U, 0x02000000U, 0x00200000U, 0x00020000U, 0x00002000U}},
{ '3', {0x60000000U, 0x06000000U, 0x00600000U, 0x00060000U, 0x00006000U}},
{ '4', {0x10000000U, 0x01000000U, 0x00100000U, 0x00010000U, 0x00001000U}},
{ '5', {0x50000000U, 0x05000000U, 0x00500000U, 0x00050000U, 0x00005000U}},
{ '6', {0x30000000U, 0x03000000U, 0x00300000U, 0x00030000U, 0x00003000U}},
{ '7', {0x70000000U, 0x07000000U, 0x00700000U, 0x00070000U, 0x00007000U}},
{ '8', {0x08000000U, 0x00800000U, 0x00080000U, 0x00008000U, 0x00000800U}},
{ '9', {0x48000000U, 0x04800000U, 0x00480000U, 0x00048000U, 0x00004800U}},
{ 'U', {0x68000000U, 0x06800000U, 0x00680000U, 0x00068000U, 0x00006800U}},
{ ' ', {0x18000000U, 0x01800000U, 0x00180000U, 0x00018000U, 0x00001800U}},
{ '-', {0x58000000U, 0x05800000U, 0x00580000U, 0x00058000U, 0x00005800U}},
{ ')', {0x38000000U, 0x03800000U, 0x00380000U, 0x00038000U, 0x00003800U}},
{ '(', {0x78000000U, 0x07800000U, 0x00780000U, 0x00078000U, 0x00007800U}},
{ 0, {0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}}
};
const uint32_t BCD_SPACES[] = {0x19999800U, 0x01999800U, 0x00199800U, 0x00019800U, 0x00001800U};
const uint32_t DATA_MASK[] = { 0x40000000U, 0x20000000U, 0x10000000U,
0x08000000U, 0x04000000U, 0x02000000U, 0x01000000U,
0x00800000U, 0x00400000U, 0x00200000U, 0x00100000U,
0x00080000U, 0x00040000U, 0x00020000U, 0x00010000U,
0x00008000U, 0x00004000U, 0x00002000U, 0x00001000U,
0x00000800U};
const unsigned char FUNCTIONAL_NUMERIC = 0U;
const unsigned char FUNCTIONAL_ALERT1 = 1U;
const unsigned char FUNCTIONAL_ALERT2 = 2U;
const unsigned char FUNCTIONAL_ALPHANUMERIC = 3U;
CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display) :
m_network(network),
m_display(display),
m_queue(5000U, "POCSAG Control"),
m_frames(0U),
m_count(0U),
m_output(),
m_buffer(),
m_ric(0U),
m_text(),
m_state(PS_NONE),
m_fp(NULL)
{
assert(display != NULL);
}
CPOCSAGControl::~CPOCSAGControl()
{
m_output.clear();
m_buffer.clear();
}
unsigned int CPOCSAGControl::readModem(unsigned char* data)
{
assert(data != NULL);
if (m_queue.isEmpty())
return 0U;
unsigned char len = 0U;
m_queue.getData(&len, 1U);
m_queue.getData(data, len);
return len;
}
bool CPOCSAGControl::processData()
{
if (m_network == NULL)
return false;
unsigned char data[300U];
unsigned int length = m_network->read(data);
if (length == 0U)
return false;
m_ric = 0U;
m_ric |= (data[0U] << 16) & 0x00FF0000U;
m_ric |= (data[1U] << 8) & 0x0000FF00U;
m_ric |= (data[2U] << 0) & 0x000000FFU;
unsigned char functional = data[3U];
m_buffer.clear();
addAddress(functional);
switch (functional) {
case FUNCTIONAL_ALPHANUMERIC:
m_text = std::string((char*)(data + 4U), length - 4U);
LogDebug("Message to %07u, func Alphanumeric: \"%s\"", m_ric, m_text.c_str());
packASCII();
break;
case FUNCTIONAL_NUMERIC:
m_text = std::string((char*)(data + 4U), length - 4U);
LogDebug("Message to %07u, func Numeric: \"%s\"", m_ric, m_text.c_str());
packNumeric();
break;
case FUNCTIONAL_ALERT1:
m_text.clear();
LogDebug("Message to %07u, func Alert 1", m_ric);
break;
case FUNCTIONAL_ALERT2:
m_text = std::string((char*)(data + 4U), length - 4U);
LogDebug("Message to %07u, func Alert 2: \"%s\"", m_ric, m_text.c_str());
packASCII();
break;
default:
break;
}
// Ensure data is an even number of words
if ((m_buffer.size() % 2U) == 1U)
m_buffer.push_back(POCSAG_IDLE_WORD);
return true;
}
void CPOCSAGControl::clock(unsigned int ms)
{
if (m_state == PS_NONE) {
bool ret = processData();
if (!ret)
return;
m_display->writePOCSAG(m_ric, m_text);
m_state = PS_WAITING;
m_frames = 0U;
m_count = 1U;
#if defined(DUMP_POCSAG)
openFile();
#endif
}
m_output.clear();
m_output.push_back(POCSAG_SYNC_WORD);
for (unsigned int i = 0U; i < POCSAG_FRAME_ADDRESSES; i++) {
if (m_state == PS_WAITING) {
if (i == (m_ric % POCSAG_FRAME_ADDRESSES)) {
uint32_t w1 = m_buffer.front();
m_buffer.pop_front();
uint32_t w2 = m_buffer.front();
m_buffer.pop_front();
m_output.push_back(w1);
m_output.push_back(w2);
m_state = PS_SENDING;
} else {
m_output.push_back(POCSAG_IDLE_WORD);
m_output.push_back(POCSAG_IDLE_WORD);
}
} else if (m_state == PS_SENDING) {
if (m_buffer.empty()) {
m_output.push_back(POCSAG_IDLE_WORD);
m_output.push_back(POCSAG_IDLE_WORD);
bool ret = processData();
if (ret) {
m_display->writePOCSAG(m_ric, m_text);
m_state = PS_WAITING;
m_count++;
} else {
m_state = PS_ENDING;
}
} else {
uint32_t w1 = m_buffer.front();
m_buffer.pop_front();
uint32_t w2 = m_buffer.front();
m_buffer.pop_front();
m_output.push_back(w1);
m_output.push_back(w2);
}
} else { // PS_ENDING
m_output.push_back(POCSAG_IDLE_WORD);
m_output.push_back(POCSAG_IDLE_WORD);
}
}
writeQueue();
m_frames++;
if (m_state == PS_ENDING) {
LogMessage("POCSAG, transmitted %u frame(s) of data from %u message(s)", m_frames, m_count);
m_display->clearPOCSAG();
m_state = PS_NONE;
#if defined(DUMP_POCSAG)
closeFile();
#endif
}
}
void CPOCSAGControl::addAddress(unsigned char functional)
{
uint32_t word = 0x00000000U;
switch (functional) {
case FUNCTIONAL_ALPHANUMERIC:
word = 0x00001800U;
break;
case FUNCTIONAL_ALERT1:
word = 0x00000800U;
break;
case FUNCTIONAL_ALERT2:
word = 0x00001000U;
break;
case FUNCTIONAL_NUMERIC:
default:
break;
}
word |= (m_ric / POCSAG_FRAME_ADDRESSES) << 13;
addBCHAndParity(word);
m_buffer.push_back(word);
}
void CPOCSAGControl::packASCII()
{
const unsigned char MASK = 0x01U;
uint32_t word = 0x80000000U;
unsigned int n = 0U;
for (std::string::const_iterator it = m_text.cbegin(); it != m_text.cend(); ++it) {
unsigned char c = *it;
for (unsigned int j = 0U; j < 7U; j++, c >>= 1) {
bool b = (c & MASK) == MASK;
if (b)
word |= DATA_MASK[n];
n++;
if (n == 20U) {
addBCHAndParity(word);
m_buffer.push_back(word);
word = 0x80000000U;
n = 0U;
}
}
}
if (n > 0U) {
addBCHAndParity(word);
m_buffer.push_back(word);
}
}
void CPOCSAGControl::packNumeric()
{
uint32_t word = 0x80000000U;
unsigned int n = 0U;
for (std::string::const_iterator it = m_text.cbegin(); it != m_text.cend(); ++it) {
char c = *it;
const BCD* bcd = NULL;
for (unsigned int i = 0U; BCD_VALUES[i].m_c != 0; i++) {
if (BCD_VALUES[i].m_c == c) {
bcd = BCD_VALUES + i;
break;
}
}
if (bcd != NULL) {
word |= bcd->m_bcd[n];
n++;
if (n == 5U) {
addBCHAndParity(word);
m_buffer.push_back(word);
word = 0x80000000U;
n = 0U;
}
}
}
// Pack the remainder of the word with BCD spaces.
if (n != 0U) {
word |= BCD_SPACES[n];
addBCHAndParity(word);
m_buffer.push_back(word);
}
}
void CPOCSAGControl::addBCHAndParity(uint32_t& word) const
{
uint32_t temp = word;
for (unsigned int i = 0U; i < 21U; i++, temp <<= 1) {
if (temp & 0x80000000U)
temp ^= 0xED200000U;
}
word |= (temp >> 21);
temp = word;
unsigned int parity = 0U;
for (unsigned int i = 0U; i < 32U; i++, temp <<= 1) {
if (temp & 0x80000000U)
parity++;
}
if ((parity % 2U) == 1U)
word |= 0x00000001U;
}
void CPOCSAGControl::writeQueue()
{
// Convert 32-bit words to bytes
unsigned char data[POCSAG_FRAME_LENGTH_BYTES];
unsigned char len = 0U;
for (std::deque<uint32_t>::const_iterator it = m_output.cbegin(); it != m_output.cend(); ++it) {
uint32_t word = *it;
data[len++] = word >> 24;
data[len++] = word >> 16;
data[len++] = word >> 8;
data[len++] = word >> 0;
}
m_output.clear();
#if defined(DUMP_POCSAG)
writeFile(data);
#endif
CUtils::dump(1U, "Data to MMDVM", data, len);
assert(len == POCSAG_FRAME_LENGTH_BYTES);
unsigned int space = m_queue.freeSpace();
if (space < (len + 1U)) {
LogError("POCSAG, overflow in the POCSAG RF queue");
return;
}
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
bool CPOCSAGControl::openFile()
{
if (m_fp != NULL)
return true;
time_t t;
::time(&t);
struct tm* tm = ::localtime(&t);
char name[100U];
::sprintf(name, "POCSAG_%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("POCSAG", 1U, 6U, m_fp);
return true;
}
bool CPOCSAGControl::writeFile(const unsigned char* data)
{
if (m_fp == NULL)
return false;
::fwrite(data, 1U, POCSAG_FRAME_LENGTH_BYTES, m_fp);
return true;
}
void CPOCSAGControl::closeFile()
{
if (m_fp != NULL) {
::fclose(m_fp);
m_fp = NULL;
}
}

74
POCSAGControl.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 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(POCSAGControl_H)
#define POCSAGControl_H
#include "POCSAGNetwork.h"
#include "POCSAGDefines.h"
#include "RingBuffer.h"
#include "Display.h"
#include "Defines.h"
#include <cstdint>
#include <string>
#include <deque>
class CPOCSAGControl {
public:
CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display);
~CPOCSAGControl();
unsigned int readModem(unsigned char* data);
void clock(unsigned int ms);
private:
CPOCSAGNetwork* m_network;
CDisplay* m_display;
CRingBuffer<unsigned char> m_queue;
unsigned int m_frames;
unsigned int m_count;
enum POCSAG_STATE {
PS_NONE,
PS_WAITING,
PS_SENDING,
PS_ENDING
};
std::deque<uint32_t> m_output;
std::deque<uint32_t> m_buffer;
uint32_t m_ric;
std::string m_text;
POCSAG_STATE m_state;
FILE* m_fp;
bool processData();
void writeQueue();
void addAddress(unsigned char functional);
void packASCII();
void packNumeric();
void addBCHAndParity(uint32_t& word) const;
bool openFile();
bool writeFile(const unsigned char* data);
void closeFile();
};
#endif

35
POCSAGDefines.h Normal file
View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2018 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(POCSAGDEFINES_H)
#define POCSAGDEFINES_H
#include <cstdint>
const unsigned int POCSAG_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate
const unsigned int POCSAG_FRAME_LENGTH_WORDS = 17U;
const unsigned int POCSAG_FRAME_LENGTH_BYTES = POCSAG_FRAME_LENGTH_WORDS * sizeof(uint32_t);
const unsigned int POCSAG_FRAME_ADDRESSES = 8U;
const uint32_t POCSAG_SYNC_WORD = 0x7CD215D8U;
const uint32_t POCSAG_IDLE_WORD = 0x7A89C197U;
#endif

122
POCSAGNetwork.cpp Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2018 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 "POCSAGDefines.h"
#include "POCSAGNetwork.h"
#include "Defines.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int BUFFER_LENGTH = 200U;
CPOCSAGNetwork::CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) :
m_socket(myAddress, myPort),
m_address(),
m_port(gatewayPort),
m_debug(debug),
m_enabled(false),
m_buffer(1000U, "POCSAG Network")
{
m_address = CUDPSocket::lookup(gatewayAddress);
}
CPOCSAGNetwork::~CPOCSAGNetwork()
{
}
bool CPOCSAGNetwork::open()
{
LogMessage("Opening POCSAG network connection");
if (m_address.s_addr == INADDR_NONE)
return false;
return m_socket.open();
}
void CPOCSAGNetwork::clock(unsigned int ms)
{
unsigned char buffer[BUFFER_LENGTH];
in_addr address;
unsigned int port;
int length = m_socket.read(buffer, BUFFER_LENGTH, address, port);
if (length <= 0)
return;
// Check if the data is for us
if (m_address.s_addr != address.s_addr || m_port != port) {
LogMessage("POCSAG packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
return;
}
// Invalid packet type?
if (::memcmp(buffer, "POCSAG", 6U) != 0)
return;
if (!m_enabled)
return;
if (m_debug)
CUtils::dump(1U, "POCSAG Network Data Received", buffer, length);
unsigned char len = length - 6U;
m_buffer.addData(&len, 1U);
m_buffer.addData(buffer + 6U, length - 6U);
}
unsigned int CPOCSAGNetwork::read(unsigned char* data)
{
assert(data != NULL);
if (m_buffer.isEmpty())
return 0U;
unsigned char len = 0U;
m_buffer.getData(&len, 1U);
m_buffer.getData(data, len);
return len;
}
void CPOCSAGNetwork::reset()
{
}
void CPOCSAGNetwork::close()
{
m_socket.close();
LogMessage("Closing POCSAG network connection");
}
void CPOCSAGNetwork::enable(bool enabled)
{
if (enabled && !m_enabled)
reset();
unsigned char c = enabled ? 0x00U : 0xFFU;
m_socket.write(&c, 1U, m_address, m_port);
m_enabled = enabled;
}

56
POCSAGNetwork.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018 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 POCSAGNetwork_H
#define POCSAGNetwork_H
#include "POCSAGDefines.h"
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
class CPOCSAGNetwork {
public:
CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug);
~CPOCSAGNetwork();
bool open();
void enable(bool enabled);
unsigned int read(unsigned char* data);
void reset();
void close();
void clock(unsigned int ms);
private:
CUDPSocket m_socket;
in_addr m_address;
unsigned int m_port;
bool m_debug;
bool m_enabled;
CRingBuffer<unsigned char> m_buffer;
};
#endif

View file

@ -1,6 +1,6 @@
These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. It supports D-Star, DMR, P25 Phase 1, NXDN, and System Fusion on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega.
These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. It supports D-Star, DMR, P25 Phase 1, NXDN, System Fusion, and POCSAG paging on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega.
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, HB Link, XLX or [DMRGateway](https://github.com/g4klx/DMRGateway) (to connect to multiple DMR networks at once) on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25 Gateway.
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, HB Link, XLX or [DMRGateway](https://github.com/g4klx/DMRGateway) (to connect to multiple DMR networks at once) on System Fusion it connects to the YSF Gateway to allow access to the FCS and YSF networks. On P25 it connects to the P25 Gateway. On NXDN it connects to the NXDN Gateway which provides access to the NXDN and NXCore talk groups. Finally it uses the DAPNET Gateway to access DAPNET to receive paging messages.
It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. It can optionally control various Displays. Currently these are:
@ -8,7 +8,7 @@ It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2
- Support for HD44780 via 4 bit GPIO connection (user selectable pins)
- Adafruit 16x2 LCD+Keypad Kits (I2C)
- Connection via PCF8574 GPIO Extender (I2C)
- Nextion TFTs (sizes 2.4", 2.8", 3.2" and 3.5")
- Nextion TFTs (all sizes, both Basic and Enhanced versions)
- TFT display sold by Hobbytronics in UK
- OLED 128x64 (SSD1306)
- LCDproc

View file

@ -35,6 +35,9 @@
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#if !defined(__APPLE__)
#include <linux/i2c-dev.h>
#endif
#endif
@ -237,119 +240,120 @@ CSerialController::~CSerialController()
bool CSerialController::open()
{
assert(m_fd == -1);
#if defined(__APPLE__)
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/
#else
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
#endif
if (m_fd < 0) {
LogError("Cannot open device - %s", m_device.c_str());
return false;
}
if (::isatty(m_fd) == 0) {
LogError("%s is not a TTY device", m_device.c_str());
::close(m_fd);
return false;
}
termios termios;
if (::tcgetattr(m_fd, &termios) < 0) {
LogError("Cannot get the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
#if defined(__APPLE__)
termios.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
termios.c_cflag &= ~CSIZE;
termios.c_cflag |= CS8; /* 8-bit characters */
termios.c_cflag &= ~PARENB; /* no parity bit */
termios.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
termios.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 1;
#else
termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
termios.c_cflag |= CS8;
termios.c_oflag &= ~(OPOST);
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 10;
#endif
switch (m_speed) {
case SERIAL_1200:
::cfsetospeed(&termios, B1200);
::cfsetispeed(&termios, B1200);
break;
case SERIAL_2400:
::cfsetospeed(&termios, B2400);
::cfsetispeed(&termios, B2400);
break;
case SERIAL_4800:
::cfsetospeed(&termios, B4800);
::cfsetispeed(&termios, B4800);
break;
case SERIAL_9600:
::cfsetospeed(&termios, B9600);
::cfsetispeed(&termios, B9600);
break;
case SERIAL_19200:
::cfsetospeed(&termios, B19200);
::cfsetispeed(&termios, B19200);
break;
case SERIAL_38400:
::cfsetospeed(&termios, B38400);
::cfsetispeed(&termios, B38400);
break;
case SERIAL_115200:
::cfsetospeed(&termios, B115200);
::cfsetispeed(&termios, B115200);
break;
case SERIAL_230400:
::cfsetospeed(&termios, B230400);
::cfsetispeed(&termios, B230400);
break;
default:
LogError("Unsupported serial port speed - %d", int(m_speed));
::close(m_fd);
if (m_fd < 0) {
LogError("Cannot open device - %s", m_device.c_str());
return false;
}
}
if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) {
LogError("Cannot set the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
if (m_assertRTS) {
unsigned int y;
if (::ioctl(m_fd, TIOCMGET, &y) < 0) {
LogError("Cannot get the control attributes for %s", m_device.c_str());
if (::isatty(m_fd) == 0) {
LogError("%s is not a TTY device", m_device.c_str());
::close(m_fd);
return false;
}
y |= TIOCM_RTS;
if (::ioctl(m_fd, TIOCMSET, &y) < 0) {
LogError("Cannot set the control attributes for %s", m_device.c_str());
termios termios;
if (::tcgetattr(m_fd, &termios) < 0) {
LogError("Cannot get the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
}
#if defined(__APPLE__)
setNonblock(false);
termios.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
termios.c_cflag &= ~CSIZE;
termios.c_cflag |= CS8; /* 8-bit characters */
termios.c_cflag &= ~PARENB; /* no parity bit */
termios.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
termios.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 1;
#else
termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
termios.c_cflag |= CS8;
termios.c_oflag &= ~(OPOST);
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 10;
#endif
switch (m_speed) {
case SERIAL_1200:
::cfsetospeed(&termios, B1200);
::cfsetispeed(&termios, B1200);
break;
case SERIAL_2400:
::cfsetospeed(&termios, B2400);
::cfsetispeed(&termios, B2400);
break;
case SERIAL_4800:
::cfsetospeed(&termios, B4800);
::cfsetispeed(&termios, B4800);
break;
case SERIAL_9600:
::cfsetospeed(&termios, B9600);
::cfsetispeed(&termios, B9600);
break;
case SERIAL_19200:
::cfsetospeed(&termios, B19200);
::cfsetispeed(&termios, B19200);
break;
case SERIAL_38400:
::cfsetospeed(&termios, B38400);
::cfsetispeed(&termios, B38400);
break;
case SERIAL_115200:
::cfsetospeed(&termios, B115200);
::cfsetispeed(&termios, B115200);
break;
case SERIAL_230400:
::cfsetospeed(&termios, B230400);
::cfsetispeed(&termios, B230400);
break;
default:
LogError("Unsupported serial port speed - %d", int(m_speed));
::close(m_fd);
return false;
}
if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) {
LogError("Cannot set the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
if (m_assertRTS) {
unsigned int y;
if (::ioctl(m_fd, TIOCMGET, &y) < 0) {
LogError("Cannot get the control attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
y |= TIOCM_RTS;
if (::ioctl(m_fd, TIOCMSET, &y) < 0) {
LogError("Cannot set the control attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
}
#if defined(__APPLE__)
setNonblock(false);
#endif
return true;
@ -380,6 +384,7 @@ int CSerialController::read(unsigned char* buffer, unsigned int length)
unsigned int offset = 0U;
while (offset < length) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_fd, &fds);

View file

@ -57,7 +57,7 @@ public:
virtual int setNonblock(bool nonblock);
#endif
private:
protected:
std::string m_device;
SERIAL_SPEED m_speed;
bool m_assertRTS;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2018 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
@ -21,21 +21,32 @@
#if defined(_WIN32) || defined(_WIN64)
CStopWatch::CStopWatch() :
m_frequency(),
m_frequencyS(),
m_frequencyMS(),
m_start()
{
::QueryPerformanceFrequency(&m_frequency);
::QueryPerformanceFrequency(&m_frequencyS);
m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL;
}
CStopWatch::~CStopWatch()
{
}
unsigned long CStopWatch::start()
unsigned long long CStopWatch::time() const
{
LARGE_INTEGER now;
::QueryPerformanceCounter(&now);
return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart);
}
unsigned long long CStopWatch::start()
{
::QueryPerformanceCounter(&m_start);
return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart);
return (unsigned long long)(m_start.QuadPart / m_frequencyS.QuadPart);
}
unsigned int CStopWatch::elapsed()
@ -46,15 +57,16 @@ unsigned int CStopWatch::elapsed()
LARGE_INTEGER temp;
temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000;
return (unsigned int)(temp.QuadPart / m_frequency.QuadPart);
return (unsigned int)(temp.QuadPart / m_frequencyS.QuadPart);
}
#else
#include <cstdio>
#include <ctime>
CStopWatch::CStopWatch() :
m_start()
m_startMS(0ULL)
{
}
@ -62,23 +74,32 @@ CStopWatch::~CStopWatch()
{
}
unsigned long CStopWatch::start()
{
::gettimeofday(&m_start, NULL);
return m_start.tv_usec;
}
unsigned int CStopWatch::elapsed()
unsigned long long CStopWatch::time() const
{
struct timeval now;
::gettimeofday(&now, NULL);
unsigned int elapsed = (now.tv_sec - m_start.tv_sec) * 1000U;
elapsed += now.tv_usec / 1000U;
elapsed -= m_start.tv_usec / 1000U;
return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL;
}
return elapsed;
unsigned long long CStopWatch::start()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
m_startMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return m_startMS;
}
unsigned int CStopWatch::elapsed()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
unsigned long long nowMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return nowMS - m_startMS;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2018 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
@ -31,15 +31,18 @@ public:
CStopWatch();
~CStopWatch();
unsigned long start();
unsigned int elapsed();
unsigned long long time() const;
unsigned long long start();
unsigned int elapsed();
private:
#if defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER m_frequency;
LARGE_INTEGER m_frequencyS;
LARGE_INTEGER m_frequencyMS;
LARGE_INTEGER m_start;
#else
struct timeval m_start;
unsigned long long m_startMS;
#endif
};

View file

@ -145,6 +145,22 @@ void CTFTSerial::setLockoutInt()
m_mode = MODE_LOCKOUT;
}
void CTFTSerial::setQuitInt()
{
// Clear the screen
clearScreen();
setFontSize(FONT_LARGE);
// Draw MMDVM logo
displayBitmap(0U, 0U, "MMDVM_sm.bmp");
gotoPosPixel(20U, 60U);
displayText("STOPPED");
m_mode = MODE_QUIT;
}
void CTFTSerial::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
@ -395,6 +411,20 @@ void CTFTSerial::clearNXDNInt()
displayText(" ");
}
void CTFTSerial::writePOCSAGInt(uint32_t ric, const std::string& message)
{
gotoPosPixel(15U, 90U);
displayText("POCSAG TX");
m_mode = MODE_CW;
}
void CTFTSerial::clearPOCSAGInt()
{
gotoPosPixel(45U, 90U);
displayText("IDLE");
}
void CTFTSerial::writeCWInt()
{
gotoPosPixel(45U, 90U);

View file

@ -39,6 +39,7 @@ protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
@ -55,6 +56,9 @@ protected:
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

View file

@ -29,6 +29,7 @@
#define PIN_YSF 5
#define PIN_P25 6
#define PIN_NXDN 7
#define PIN_POCSAG 8
#define PIN_TX 10
#define PIN_CD 11
@ -64,17 +65,19 @@ void setup()
pinMode(PIN_YSF, OUTPUT);
pinMode(PIN_P25, OUTPUT);
pinMode(PIN_NXDN, OUTPUT);
pinMode(PIN_POCSAG, OUTPUT);
pinMode(PIN_TX, OUTPUT);
pinMode(PIN_CD, OUTPUT);
pinMode(PIN_LOCKOUT, INPUT);
digitalWrite(PIN_DSTAR, LOW);
digitalWrite(PIN_DMR, LOW);
digitalWrite(PIN_YSF, LOW);
digitalWrite(PIN_P25, LOW);
digitalWrite(PIN_NXDN, LOW);
digitalWrite(PIN_TX, LOW);
digitalWrite(PIN_CD, LOW);
digitalWrite(PIN_DSTAR, LOW);
digitalWrite(PIN_DMR, LOW);
digitalWrite(PIN_YSF, LOW);
digitalWrite(PIN_P25, LOW);
digitalWrite(PIN_NXDN, LOW);
digitalWrite(PIN_POCSAG, LOW);
digitalWrite(PIN_TX, LOW);
digitalWrite(PIN_CD, LOW);
}
#define UMP_FRAME_START 0xF0U
@ -95,6 +98,7 @@ void setup()
#define MODE_YSF 3U
#define MODE_P25 4U
#define MODE_NXDN 5U
#define MODE_POCSAG 6U
bool m_started = false;
uint32_t m_count = 0U;
@ -129,11 +133,12 @@ void loop()
m_started = true;
break;
case UMP_SET_MODE:
digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW);
digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW);
digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW);
digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW);
digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW);
digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW);
digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW);
digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW);
digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW);
digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW);
digitalWrite(PIN_POCSAG, m_buffer[3U] == MODE_POCSAG ? HIGH : LOW);
break;
case UMP_SET_TX:
digitalWrite(PIN_TX, m_buffer[3U] == 0x01U ? HIGH : LOW);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018 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
@ -19,6 +19,6 @@
#if !defined(VERSION_H)
#define VERSION_H
const char* VERSION = "20180327";
const char* VERSION = "20180723";
#endif

View file

@ -52,8 +52,8 @@
DMRIDPATH=/path/to/DMR/ID/file
DMRIDFILE=${DMRIDPATH}/DMRIds.dat
# DMR-MARC.net has discontinued real time access. Now they offer a nightly dump instead.
DATABASEURL='https://www.dmr-marc.net/static/users.csv'
# DMR IDs now served by RadioID.net
DATABASEURL='https://ham-digital.org/status/users.csv'
#
# How many DMR ID files do you want backed up (0 = do not keep backups)