Merge pull request #1 from g4klx/master

update my repo from original
This commit is contained in:
George Smart 2017-03-07 05:25:33 +00:00 committed by GitHub
commit e3a8715660
95 changed files with 60075 additions and 45915 deletions

283
Conf.cpp
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -35,6 +35,7 @@ enum SECTION {
SECTION_CWID,
SECTION_DMRID_LOOKUP,
SECTION_MODEM,
SECTION_UMP,
SECTION_DSTAR,
SECTION_DMR,
SECTION_FUSION,
@ -47,6 +48,7 @@ enum SECTION {
SECTION_HD44780,
SECTION_NEXTION,
SECTION_OLED,
SECTION_LCDPROC,
SECTION_TGREWRITE
};
@ -89,32 +91,28 @@ m_modemDMRTXLevel(50U),
m_modemYSFTXLevel(50U),
m_modemP25TXLevel(50U),
m_modemOscOffset(0),
m_modemRSSIMultiplier(0),
m_modemRSSIOffset(0),
m_modemRSSIMappingFile(),
m_modemSamplesDir(),
m_modemDebug(false),
m_umpEnabled(false),
m_umpPort(),
m_dstarEnabled(false),
m_dstarModule("C"),
m_dstarSelfOnly(false),
m_dstarBlackList(),
m_dstarErrorReply(true),
m_dmrEnabled(false),
m_dmrBeacons(false),
m_dmrId(0U),
m_dmrColorCode(2U),
m_dmrSelfOnly(false),
m_dmrTGRewriteSlot1(false),
m_dmrTGRewriteSlot2(false),
m_dmrBMAutoRewrite(false),
m_dmrBMRewriteReflectorVoicePrompts(false),
m_dmrEmbeddedLCOnly(false),
m_dmrDumpTAData(true),
m_dmrPrefixes(),
m_dmrBlackList(),
m_dmrDstIdBlacklistSlot1RF(),
m_dmrDstIdBlacklistSlot2RF(),
m_dmrDstIdWhitelistSlot1RF(),
m_dmrDstIdWhitelistSlot2RF(),
m_dmrDstIdBlacklistSlot1NET(),
m_dmrDstIdBlacklistSlot2NET(),
m_dmrDstIdWhitelistSlot1NET(),
m_dmrDstIdWhitelistSlot2NET(),
m_dmrWhiteList(),
m_dmrSlot1TGWhiteList(),
m_dmrSlot2TGWhiteList(),
m_dmrCallHang(3U),
m_dmrTXHang(4U),
m_fusionEnabled(false),
@ -131,11 +129,11 @@ m_dmrNetworkAddress(),
m_dmrNetworkPort(0U),
m_dmrNetworkLocal(0U),
m_dmrNetworkPassword(),
m_dmrNetworkOptions(),
m_dmrNetworkDebug(false),
m_dmrNetworkJitter(300U),
m_dmrNetworkSlot1(true),
m_dmrNetworkSlot2(true),
m_dmrNetworkRSSI(false),
m_fusionNetworkEnabled(false),
m_fusionNetworkMyAddress(),
m_fusionNetworkMyPort(0U),
@ -164,9 +162,14 @@ m_nextionBrightness(50U),
m_nextionDisplayClock(false),
m_nextionUTC(false),
m_nextionIdleBrightness(20U),
m_oledType(3),
m_oledBrightness(0),
m_oledInvert(0)
m_oledType(3U),
m_oledBrightness(0U),
m_oledInvert(false),
m_lcdprocAddress(),
m_lcdprocPort(0U),
m_lcdprocLocalPort(0U),
m_lcdprocDisplayClock(false),
m_lcdprocUTC(false)
{
}
@ -201,7 +204,9 @@ bool CConf::read()
else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0)
section = SECTION_DMRID_LOOKUP;
else if (::strncmp(buffer, "[Modem]", 7U) == 0)
section = SECTION_MODEM;
section = SECTION_MODEM;
else if (::strncmp(buffer, "[UMP]", 5U) == 0)
section = SECTION_UMP;
else if (::strncmp(buffer, "[D-Star]", 8U) == 0)
section = SECTION_DSTAR;
else if (::strncmp(buffer, "[DMR]", 5U) == 0)
@ -211,24 +216,25 @@ bool CConf::read()
else if (::strncmp(buffer, "[P25]", 5U) == 0)
section = SECTION_P25;
else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0)
section = SECTION_DSTAR_NETWORK;
section = SECTION_DSTAR_NETWORK;
else if (::strncmp(buffer, "[DMR Network]", 13U) == 0)
section = SECTION_DMR_NETWORK;
section = SECTION_DMR_NETWORK;
else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0)
section = SECTION_FUSION_NETWORK;
section = SECTION_FUSION_NETWORK;
else if (::strncmp(buffer, "[P25 Network]", 13U) == 0)
section = SECTION_P25_NETWORK;
else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0)
section = SECTION_TFTSERIAL;
section = SECTION_TFTSERIAL;
else if (::strncmp(buffer, "[HD44780]", 9U) == 0)
section = SECTION_HD44780;
else if (::strncmp(buffer, "[Nextion]", 9U) == 0)
section = SECTION_NEXTION;
else if (::strncmp(buffer, "[OLED]", 6U) == 0)
section = SECTION_OLED;
else if (::strncmp(buffer, "[LCDproc]", 9U) == 0)
section = SECTION_LCDPROC;
else
section = SECTION_NONE;
section = SECTION_NONE;
continue;
}
@ -325,12 +331,17 @@ bool CConf::read()
m_modemP25TXLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "OscOffset") == 0)
m_modemOscOffset = ::atoi(value);
else if (::strcmp(key, "RSSIMultiplier") == 0)
m_modemRSSIMultiplier = ::atoi(value);
else if (::strcmp(key, "RSSIOffset") == 0)
m_modemRSSIOffset = ::atoi(value);
else if (::strcmp(key, "RSSIMappingFile") == 0)
m_modemRSSIMappingFile = value;
else if (::strcmp(key, "SamplesDir") == 0)
m_modemSamplesDir = value;
else if (::strcmp(key, "Debug") == 0)
m_modemDebug = ::atoi(value) == 1;
} else if (section == SECTION_UMP) {
if (::strcmp(key, "Enable") == 0)
m_umpEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Port") == 0)
m_umpPort = value;
} else if (section == SECTION_DSTAR) {
if (::strcmp(key, "Enable") == 0)
m_dstarEnabled = ::atoi(value) == 1;
@ -353,7 +364,8 @@ bool CConf::read()
}
p = ::strtok(NULL, ",\r\n");
}
}
} else if (::strcmp(key, "ErrorReply") == 0)
m_dstarErrorReply = ::atoi(value) == 1;
} else if (section == SECTION_DMR) {
if (::strcmp(key, "Enable") == 0)
m_dmrEnabled = ::atoi(value) == 1;
@ -365,6 +377,10 @@ bool CConf::read()
m_dmrColorCode = (unsigned int)::atoi(value);
else if (::strcmp(key, "SelfOnly") == 0)
m_dmrSelfOnly = ::atoi(value) == 1;
else if (::strcmp(key, "EmbeddedLCOnly") == 0)
m_dmrEmbeddedLCOnly = ::atoi(value) == 1;
else if (::strcmp(key, "DumpTAData") == 0)
m_dmrDumpTAData = ::atoi(value) == 1;
else if (::strcmp(key, "Prefixes") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
@ -381,82 +397,34 @@ bool CConf::read()
m_dmrBlackList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdBlackListSlot1RF") == 0) {
} else if (::strcmp(key, "WhiteList") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdBlacklistSlot1RF.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdBlackListSlot2RF") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdBlacklistSlot2RF.push_back(id);
m_dmrWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdWhiteListSlot1RF") == 0) {
} else if (::strcmp(key, "Slot1TGWhiteList") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdWhitelistSlot1RF.push_back(id);
m_dmrSlot1TGWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdWhiteListSlot2RF") == 0) {
} else if (::strcmp(key, "Slot2TGWhiteList") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdWhitelistSlot2RF.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdBlackListSlot1NET") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdBlacklistSlot1NET.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdBlackListSlot2NET") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdBlacklistSlot2NET.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdWhiteListSlot1NET") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdWhitelistSlot1NET.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "DstIdWhiteListSlot2NET") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrDstIdWhitelistSlot2NET.push_back(id);
m_dmrSlot2TGWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "TXHang") == 0)
m_dmrTXHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "CallHang") == 0)
m_dmrCallHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "TGRewriteSlot1") == 0)
m_dmrTGRewriteSlot1 = ::atoi(value) == 1;
else if (::strcmp(key, "TGRewriteSlot2") == 0)
m_dmrTGRewriteSlot2 = ::atoi(value) == 1;
else if (::strcmp(key, "BMAutoRewrite") == 0)
m_dmrBMAutoRewrite = ::atoi(value) == 1;
else if (::strcmp(key, "BMRewriteReflectorVoicePrompts") == 0)
m_dmrBMRewriteReflectorVoicePrompts = ::atoi(value) == 1;
} else if (section == SECTION_FUSION) {
if (::strcmp(key, "Enable") == 0)
m_fusionEnabled = ::atoi(value) == 1;
@ -489,6 +457,8 @@ bool CConf::read()
m_dmrNetworkLocal = (unsigned int)::atoi(value);
else if (::strcmp(key, "Password") == 0)
m_dmrNetworkPassword = value;
else if (::strcmp(key, "Options") == 0)
m_dmrNetworkOptions = value;
else if (::strcmp(key, "Debug") == 0)
m_dmrNetworkDebug = ::atoi(value) == 1;
else if (::strcmp(key, "Jitter") == 0)
@ -497,8 +467,6 @@ bool CConf::read()
m_dmrNetworkSlot1 = ::atoi(value) == 1;
else if (::strcmp(key, "Slot2") == 0)
m_dmrNetworkSlot2 = ::atoi(value) == 1;
else if (::strcmp(key, "RSSI") == 0)
m_dmrNetworkRSSI = ::atoi(value) == 1;
} else if (section == SECTION_FUSION_NETWORK) {
if (::strcmp(key, "Enable") == 0)
m_fusionNetworkEnabled = ::atoi(value) == 1;
@ -569,13 +537,24 @@ bool CConf::read()
} else if (section == SECTION_OLED) {
if (::strcmp(key, "Type") == 0)
m_oledType = (unsigned char)::atoi(value);
else if (::strcmp(key, "Port") == 0)
m_oledBrightness = (unsigned char)::atoi(value);
else if (::strcmp(key, "Brightness") == 0)
m_oledInvert = (unsigned char)::atoi(value);
m_oledBrightness = (unsigned char)::atoi(value);
else if (::strcmp(key, "Invert") == 0)
m_oledInvert = ::atoi(value) == 1;
} else if (section == SECTION_LCDPROC) {
if (::strcmp(key, "Address") == 0)
m_lcdprocAddress = value;
else if (::strcmp(key, "Port") == 0)
m_lcdprocPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "LocalPort") == 0)
m_lcdprocLocalPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "DisplayClock") == 0)
m_lcdprocDisplayClock = ::atoi(value) == 1;
else if (::strcmp(key, "UTC") == 0)
m_lcdprocUTC = ::atoi(value) == 1;
else if (::strcmp(key, "DimOnIdle") == 0)
m_lcdprocDimOnIdle = ::atoi(value) == 1;
}
}
::fclose(fp);
@ -768,14 +747,14 @@ int CConf::getModemOscOffset() const
return m_modemOscOffset;
}
int CConf::getModemRSSIMultiplier () const
std::string CConf::getModemRSSIMappingFile () const
{
return m_modemRSSIMultiplier;
return m_modemRSSIMappingFile;
}
int CConf::getModemRSSIOffset() const
std::string CConf::getModemSamplesDir() const
{
return m_modemRSSIOffset;
return m_modemSamplesDir;
}
bool CConf::getModemDebug() const
@ -783,6 +762,16 @@ bool CConf::getModemDebug() const
return m_modemDebug;
}
bool CConf::getUMPEnabled() const
{
return m_umpEnabled;
}
std::string CConf::getUMPPort() const
{
return m_umpPort;
}
bool CConf::getDStarEnabled() const
{
return m_dstarEnabled;
@ -803,6 +792,11 @@ std::vector<std::string> CConf::getDStarBlackList() const
return m_dstarBlackList;
}
bool CConf::getDStarErrorReply() const
{
return m_dstarErrorReply;
}
bool CConf::getDMREnabled() const
{
return m_dmrEnabled;
@ -828,24 +822,14 @@ bool CConf::getDMRSelfOnly() const
return m_dmrSelfOnly;
}
bool CConf::getDMRTGRewriteSlot1() const
bool CConf::getDMREmbeddedLCOnly() const
{
return m_dmrTGRewriteSlot1;
return m_dmrEmbeddedLCOnly;
}
bool CConf::getDMRTGRewriteSlot2() const
bool CConf::getDMRDumpTAData() const
{
return m_dmrTGRewriteSlot2;
}
bool CConf::getDMRBMAutoRewrite() const
{
return m_dmrBMAutoRewrite;
}
bool CConf::getDMRBMRewriteReflectorVoicePrompts() const
{
return m_dmrBMRewriteReflectorVoicePrompts;
return m_dmrDumpTAData;
}
std::vector<unsigned int> CConf::getDMRPrefixes() const
@ -858,44 +842,19 @@ std::vector<unsigned int> CConf::getDMRBlackList() const
return m_dmrBlackList;
}
std::vector<unsigned int> CConf::getDMRDstIdBlacklistSlot1RF() const
std::vector<unsigned int> CConf::getDMRWhiteList() const
{
return m_dmrDstIdBlacklistSlot1RF;
return m_dmrWhiteList;
}
std::vector<unsigned int> CConf::getDMRDstIdBlacklistSlot2RF() const
std::vector<unsigned int> CConf::getDMRSlot1TGWhiteList() const
{
return m_dmrDstIdBlacklistSlot2RF;
return m_dmrSlot1TGWhiteList;
}
std::vector<unsigned int> CConf::getDMRDstIdWhitelistSlot1RF() const
std::vector<unsigned int> CConf::getDMRSlot2TGWhiteList() const
{
return m_dmrDstIdWhitelistSlot1RF;
}
std::vector<unsigned int> CConf::getDMRDstIdWhitelistSlot2RF() const
{
return m_dmrDstIdWhitelistSlot2RF;
}
std::vector<unsigned int> CConf::getDMRDstIdBlacklistSlot1NET() const
{
return m_dmrDstIdBlacklistSlot1NET;
}
std::vector<unsigned int> CConf::getDMRDstIdBlacklistSlot2NET() const
{
return m_dmrDstIdBlacklistSlot2NET;
}
std::vector<unsigned int> CConf::getDMRDstIdWhitelistSlot1NET() const
{
return m_dmrDstIdWhitelistSlot1NET;
}
std::vector<unsigned int> CConf::getDMRDstIdWhitelistSlot2NET() const
{
return m_dmrDstIdWhitelistSlot2NET;
return m_dmrSlot2TGWhiteList;
}
unsigned int CConf::getDMRCallHang() const
@ -978,6 +937,11 @@ std::string CConf::getDMRNetworkPassword() const
return m_dmrNetworkPassword;
}
std::string CConf::getDMRNetworkOptions() const
{
return m_dmrNetworkOptions;
}
bool CConf::getDMRNetworkDebug() const
{
return m_dmrNetworkDebug;
@ -998,11 +962,6 @@ bool CConf::getDMRNetworkSlot2() const
return m_dmrNetworkSlot2;
}
bool CConf::getDMRNetworkRSSI() const
{
return m_dmrNetworkRSSI;
}
bool CConf::getFusionNetworkEnabled() const
{
return m_fusionNetworkEnabled;
@ -1153,7 +1112,37 @@ unsigned char CConf::getOLEDBrightness() const
return m_oledBrightness;
}
unsigned char CConf::getOLEDInvert() const
bool CConf::getOLEDInvert() const
{
return m_oledInvert;
}
std::string CConf::getLCDprocAddress() const
{
return m_lcdprocAddress;
}
unsigned int CConf::getLCDprocPort() const
{
return m_lcdprocPort;
}
unsigned int CConf::getLCDprocLocalPort() const
{
return m_lcdprocLocalPort;
}
bool CConf::getLCDprocDisplayClock() const
{
return m_lcdprocDisplayClock;
}
bool CConf::getLCDprocUTC() const
{
return m_lcdprocUTC;
}
bool CConf::getLCDprocDimOnIdle() const
{
return m_lcdprocDimOnIdle;
}

76
Conf.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -78,36 +78,34 @@ public:
unsigned int getModemYSFTXLevel() const;
unsigned int getModemP25TXLevel() const;
int getModemOscOffset() const;
int getModemRSSIMultiplier() const;
int getModemRSSIOffset() const;
std::string getModemRSSIMappingFile() const;
std::string getModemSamplesDir() const;
bool getModemDebug() const;
// The UMP section
bool getUMPEnabled() const;
std::string getUMPPort() const;
// The D-Star section
bool getDStarEnabled() const;
std::string getDStarModule() const;
bool getDStarSelfOnly() const;
std::vector<std::string> getDStarBlackList() const;
bool getDStarErrorReply() const;
// The DMR section
bool getDMREnabled() const;
bool getDMRBeacons() const;
unsigned int getDMRId() const;
unsigned int getDMRColorCode() const;
bool getDMREmbeddedLCOnly() const;
bool getDMRDumpTAData() const;
bool getDMRSelfOnly() const;
bool getDMRTGRewriteSlot1() const;
bool getDMRTGRewriteSlot2() const;
bool getDMRBMAutoRewrite() const;
bool getDMRBMRewriteReflectorVoicePrompts() const;
std::vector<unsigned int> getDMRPrefixes() const;
std::vector<unsigned int> getDMRBlackList() const;
std::vector<unsigned int> getDMRDstIdBlacklistSlot1RF() const;
std::vector<unsigned int> getDMRDstIdBlacklistSlot2RF() const;
std::vector<unsigned int> getDMRDstIdWhitelistSlot1RF() const;
std::vector<unsigned int> getDMRDstIdWhitelistSlot2RF() const;
std::vector<unsigned int> getDMRDstIdBlacklistSlot1NET() const;
std::vector<unsigned int> getDMRDstIdBlacklistSlot2NET() const;
std::vector<unsigned int> getDMRDstIdWhitelistSlot1NET() const;
std::vector<unsigned int> getDMRDstIdWhitelistSlot2NET() const;
std::vector<unsigned int> getDMRWhiteList() const;
std::vector<unsigned int> getDMRSlot1TGWhiteList() const;
std::vector<unsigned int> getDMRSlot2TGWhiteList() const;
unsigned int getDMRCallHang() const;
unsigned int getDMRTXHang() const;
@ -132,11 +130,11 @@ public:
unsigned int getDMRNetworkPort() const;
unsigned int getDMRNetworkLocal() const;
std::string getDMRNetworkPassword() const;
std::string getDMRNetworkOptions() const;
bool getDMRNetworkDebug() const;
unsigned int getDMRNetworkJitter() const;
bool getDMRNetworkSlot1() const;
bool getDMRNetworkSlot2() const;
bool getDMRNetworkRSSI() const;
// The System Fusion Network section
bool getFusionNetworkEnabled() const;
@ -179,7 +177,15 @@ public:
// The OLED section
unsigned char getOLEDType() const;
unsigned char getOLEDBrightness() const;
unsigned char getOLEDInvert() const;
bool getOLEDInvert() const;
// The LCDproc section
std::string getLCDprocAddress() const;
unsigned int getLCDprocPort() const;
unsigned int getLCDprocLocalPort() const;
bool getLCDprocDisplayClock() const;
bool getLCDprocUTC() const;
bool getLCDprocDimOnIdle() const;
private:
std::string m_file;
@ -225,34 +231,31 @@ private:
unsigned int m_modemYSFTXLevel;
unsigned int m_modemP25TXLevel;
int m_modemOscOffset;
int m_modemRSSIMultiplier;
int m_modemRSSIOffset;
std::string m_modemRSSIMappingFile;
std::string m_modemSamplesDir;
bool m_modemDebug;
bool m_umpEnabled;
std::string m_umpPort;
bool m_dstarEnabled;
std::string m_dstarModule;
bool m_dstarSelfOnly;
std::vector<std::string> m_dstarBlackList;
bool m_dstarErrorReply;
bool m_dmrEnabled;
bool m_dmrBeacons;
unsigned int m_dmrId;
unsigned int m_dmrColorCode;
bool m_dmrSelfOnly;
bool m_dmrTGRewriteSlot1;
bool m_dmrTGRewriteSlot2;
bool m_dmrBMAutoRewrite;
bool m_dmrBMRewriteReflectorVoicePrompts;
bool m_dmrEmbeddedLCOnly;
bool m_dmrDumpTAData;
std::vector<unsigned int> m_dmrPrefixes;
std::vector<unsigned int> m_dmrBlackList;
std::vector<unsigned int> m_dmrDstIdBlacklistSlot1RF;
std::vector<unsigned int> m_dmrDstIdBlacklistSlot2RF;
std::vector<unsigned int> m_dmrDstIdWhitelistSlot1RF;
std::vector<unsigned int> m_dmrDstIdWhitelistSlot2RF;
std::vector<unsigned int> m_dmrDstIdBlacklistSlot1NET;
std::vector<unsigned int> m_dmrDstIdBlacklistSlot2NET;
std::vector<unsigned int> m_dmrDstIdWhitelistSlot1NET;
std::vector<unsigned int> m_dmrDstIdWhitelistSlot2NET;
std::vector<unsigned int> m_dmrWhiteList;
std::vector<unsigned int> m_dmrSlot1TGWhiteList;
std::vector<unsigned int> m_dmrSlot2TGWhiteList;
unsigned int m_dmrCallHang;
unsigned int m_dmrTXHang;
@ -273,11 +276,11 @@ private:
unsigned int m_dmrNetworkPort;
unsigned int m_dmrNetworkLocal;
std::string m_dmrNetworkPassword;
std::string m_dmrNetworkOptions;
bool m_dmrNetworkDebug;
unsigned int m_dmrNetworkJitter;
bool m_dmrNetworkSlot1;
bool m_dmrNetworkSlot2;
bool m_dmrNetworkRSSI;
bool m_fusionNetworkEnabled;
std::string m_fusionNetworkMyAddress;
@ -314,7 +317,14 @@ private:
unsigned char m_oledType;
unsigned char m_oledBrightness;
unsigned char m_oledInvert;
bool m_oledInvert;
std::string m_lcdprocAddress;
unsigned int m_lcdprocPort;
unsigned int m_lcdprocLocalPort;
bool m_lcdprocDisplayClock;
bool m_lcdprocUTC;
bool m_lcdprocDimOnIdle;
};
#endif

View file

@ -1,4 +1,7 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016,2017 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
@ -20,231 +23,71 @@
#include <vector>
#include <cstring>
std::vector<unsigned int> CDMRAccessControl::m_blackList;
std::vector<unsigned int> CDMRAccessControl::m_whiteList;
std::vector<unsigned int> DMRAccessControl::m_dstBlackListSlot1RF;
std::vector<unsigned int> DMRAccessControl::m_dstBlackListSlot2RF;
std::vector<unsigned int> DMRAccessControl::m_dstWhiteListSlot1RF;
std::vector<unsigned int> DMRAccessControl::m_dstWhiteListSlot2RF;
std::vector<unsigned int> CDMRAccessControl::m_prefixes;
std::vector<unsigned int> DMRAccessControl::m_dstBlackListSlot1NET;
std::vector<unsigned int> DMRAccessControl::m_dstBlackListSlot2NET;
std::vector<unsigned int> DMRAccessControl::m_dstWhiteListSlot1NET;
std::vector<unsigned int> DMRAccessControl::m_dstWhiteListSlot2NET;
std::vector<unsigned int> CDMRAccessControl::m_slot1TGWhiteList;
std::vector<unsigned int> CDMRAccessControl::m_slot2TGWhiteList;
std::vector<unsigned int> DMRAccessControl::m_srcIdBlacklist;
bool CDMRAccessControl::m_selfOnly = false;
std::vector<unsigned int> DMRAccessControl::m_prefixes;
unsigned int CDMRAccessControl::m_id = 0U;
bool DMRAccessControl::m_selfOnly = false;
unsigned int DMRAccessControl::m_id = 0U;
unsigned int DMRAccessControl::m_dstRewriteID = 0U;
unsigned int DMRAccessControl::m_srcID = 0U;
CDMRLC* DMRAccessControl::m_lastdmrLC;
time_t DMRAccessControl::m_time;
int DMRAccessControl::m_callHang;
bool DMRAccessControl::m_tgRewriteSlot1;
bool DMRAccessControl::m_tgRewriteSlot2;
bool DMRAccessControl::m_bmAutoRewrite;
bool DMRAccessControl::m_bmRewriteReflectorVoicePrompts;
void DMRAccessControl::init(const std::vector<unsigned int>& dstIdBlacklistSlot1RF, const std::vector<unsigned int>& dstIdWhitelistSlot1RF, const std::vector<unsigned int>& dstIdBlacklistSlot2RF, const std::vector<unsigned int>& dstIdWhitelistSlot2RF, const std::vector<unsigned int>& dstIdBlacklistSlot1NET, const std::vector<unsigned int>& dstIdWhitelistSlot1NET, const std::vector<unsigned int>& dstIdBlacklistSlot2NET, const std::vector<unsigned int>& dstIdWhitelistSlot2NET, const std::vector<unsigned int>& srcIdBlacklist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id, unsigned int callHang, bool tgRewriteSlot1, bool tgRewriteSlot2, bool bmAutoRewrite, bool bmRewriteReflectorVoicePrompts)
void CDMRAccessControl::init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id)
{
m_dstBlackListSlot1RF = dstIdBlacklistSlot1RF;
m_dstWhiteListSlot1RF = dstIdWhitelistSlot1RF;
m_dstBlackListSlot2RF = dstIdBlacklistSlot2RF;
m_dstWhiteListSlot2RF = dstIdWhitelistSlot2RF;
m_dstBlackListSlot1NET = dstIdBlacklistSlot1NET;
m_dstWhiteListSlot1NET = dstIdWhitelistSlot1NET;
m_dstBlackListSlot2NET = dstIdBlacklistSlot2NET;
m_dstWhiteListSlot2NET = dstIdWhitelistSlot2NET;
m_callHang = callHang;
m_tgRewriteSlot1 = tgRewriteSlot1;
m_tgRewriteSlot2 = tgRewriteSlot2;
m_bmAutoRewrite = bmAutoRewrite;
m_bmRewriteReflectorVoicePrompts = bmRewriteReflectorVoicePrompts;
m_slot1TGWhiteList = slot1TGWhitelist;
m_slot2TGWhiteList = slot2TGWhitelist;
m_blackList = blacklist;
m_whiteList = whitelist;
m_selfOnly = selfOnly;
m_prefixes = prefixes;
m_id = id;
}
bool DMRAccessControl::dstIdBlacklist(unsigned int did, unsigned int slot, bool network)
bool CDMRAccessControl::validateSrcId(unsigned int id)
{
static std::vector<unsigned int> blacklist;
if (slot == 1U) {
if (network)
blacklist = m_dstBlackListSlot1NET;
else
blacklist = m_dstBlackListSlot1RF;
} else {
if (network)
blacklist = m_dstBlackListSlot2NET;
else
blacklist = m_dstBlackListSlot2RF;
}
return std::find(blacklist.begin(), blacklist.end(), did) != blacklist.end();
}
bool DMRAccessControl::dstIdWhitelist(unsigned int did, unsigned int slot, bool gt4k, bool network)
{
if (network) {
if (slot == 1U) {
if (m_dstWhiteListSlot1NET.size() == 0U)
return true;
// No reflectors on slot1, so we only allow all IDs over 99999 unless specifically whitelisted.
// Allow traffic to TG0 as I think this is a special case - need to confirm
if (gt4k) {
if (std::find(m_dstWhiteListSlot1NET.begin(), m_dstWhiteListSlot1NET.end(), did) != m_dstWhiteListSlot1NET.end() || did >= 99999U || did == 0)
return true;
} else {
if (std::find(m_dstWhiteListSlot1NET.begin(), m_dstWhiteListSlot1NET.end(), did) != m_dstWhiteListSlot1NET.end() || did == 0)
return true;
}
} else {
if (m_dstWhiteListSlot2NET.size() == 0U)
return true;
// On slot2 we allow reflector control IDs, but not secondary TG IDs unless specifically listed. Also allow echo.
if (gt4k) {
if (std::find(m_dstWhiteListSlot2NET.begin(), m_dstWhiteListSlot2NET.end(), did) != m_dstWhiteListSlot2NET.end() || did == 0)
return true;
// If dstId in secondary TG range or whitelist
else if (did >= 4000) {
if (did > 5000U && did < 10000U)
return false;
else
return true;
}
} else {
if (std::find(m_dstWhiteListSlot2NET.begin(), m_dstWhiteListSlot2NET.end(), did) != m_dstWhiteListSlot2NET.end())
return true;
}
}
return false;
} else {
if (slot == 1U) {
if (m_dstWhiteListSlot1RF.size() == 0U)
return true;
// No reflectors on slot1, so we only allow all IDs over 99999 unless specifically whitelisted.
// Allow traffic to TG0 as I think this is a special case - need to confirm
if (gt4k) {
if (std::find(m_dstWhiteListSlot1RF.begin(), m_dstWhiteListSlot1RF.end(), did) != m_dstWhiteListSlot1RF.end() || did >= 99999U || did == 0)
return true;
} else {
if (std::find(m_dstWhiteListSlot1RF.begin(), m_dstWhiteListSlot1RF.end(), did) != m_dstWhiteListSlot1RF.end() || did == 0)
return true;
}
} else {
if (m_dstWhiteListSlot2RF.size() == 0U)
return true;
// On slot2 we allow reflector control IDs, but not secondary TG IDs unless specifically listed. Also allow echo.
if (gt4k) {
if (std::find(m_dstWhiteListSlot2RF.begin(), m_dstWhiteListSlot2RF.end(), did) != m_dstWhiteListSlot2RF.end() || did == 0)
return true;
// If dstId in secondary TG range or whitelist
else if (did >= 4000U) {
if (did > 5000U && did < 10000U)
return false;
else
return true;
}
} else {
if (std::find(m_dstWhiteListSlot2RF.begin(), m_dstWhiteListSlot2RF.end(), did) != m_dstWhiteListSlot2RF.end())
return true;
}
}
return false;
}
}
bool DMRAccessControl::validateSrcId(unsigned int id)
{
if (m_selfOnly) {
if (m_selfOnly)
return id == m_id;
} else {
if (std::find(m_srcIdBlacklist.begin(), m_srcIdBlacklist.end(), id) != m_srcIdBlacklist.end())
return false;
unsigned int prefix = id / 10000U;
if (prefix == 0U || prefix > 999U)
return false;
if (std::find(m_blackList.begin(), m_blackList.end(), id) != m_blackList.end())
return false;
if (m_prefixes.size() == 0U)
unsigned int prefix = id / 10000U;
if (prefix == 0U || prefix > 999U)
return false;
if (!m_prefixes.empty()) {
bool ret = std::find(m_prefixes.begin(), m_prefixes.end(), prefix) == m_prefixes.end();
if (ret)
return false;
}
if (!m_whiteList.empty())
return std::find(m_whiteList.begin(), m_whiteList.end(), id) != m_whiteList.end();
return true;
}
bool CDMRAccessControl::validateTGId(unsigned int slotNo, bool group, unsigned int id)
{
if (!group)
return true;
// TG0 is never valid
if (id == 0U)
return false;
if (slotNo == 1U) {
if (m_slot1TGWhiteList.empty())
return true;
return std::find(m_prefixes.begin(), m_prefixes.end(), prefix) != m_prefixes.end();
}
}
bool DMRAccessControl::validateAccess(unsigned int src_id, unsigned int dst_id, unsigned int slot, bool network)
{
// source ID validation is only applied to RF traffic
if (!network && !DMRAccessControl::validateSrcId(src_id)) {
LogMessage("DMR Slot %u, invalid access attempt from %u (blacklisted)", slot, src_id);
return false;
} else if (DMRAccessControl::dstIdBlacklist(dst_id, slot, network)) {
LogMessage("DMR Slot %u, invalid access attempt to TG%u (TG blacklisted)", slot, dst_id);
return false;
} else if (!DMRAccessControl::dstIdWhitelist(dst_id, slot, true, network)) {
LogMessage("DMR Slot %u, invalid access attempt to TG%u (TG not in whitelist)", slot, dst_id);
return false;
return std::find(m_slot1TGWhiteList.begin(), m_slot1TGWhiteList.end(), id) != m_slot1TGWhiteList.end();
} else {
return true;
if (m_slot2TGWhiteList.empty())
return true;
return std::find(m_slot2TGWhiteList.begin(), m_slot2TGWhiteList.end(), id) != m_slot2TGWhiteList.end();
}
}
unsigned int DMRAccessControl::dstIdRewrite(unsigned int did, unsigned int sid, unsigned int slot, bool network, CDMRLC* dmrLC)
{
if (slot == 1U && !m_tgRewriteSlot1)
return 0U;
if (slot == 2U && !m_tgRewriteSlot2)
return 0U;
time_t currenttime = ::time(NULL);
if (network) {
m_dstRewriteID = did;
m_srcID = sid;
//not needed at present - for direct dial, which requires change at master end.
//memcpy(&m_lastdmrLC, &dmrLC, sizeof(dmrLC));
if (m_bmAutoRewrite && (did < 4000U || did > 5000U) && did > 0U && did != 9U && dmrLC->getFLCO() == FLCO_GROUP ) {
LogMessage("DMR Slot %u, Rewrite DST ID (TG) of of inbound network traffic from %u to 9",slot,did);
return 9U;
// rewrite incoming BM voice prompts to TG 9
} else if (m_bmRewriteReflectorVoicePrompts && (sid >= 4000U && sid <= 5000U) && dmrLC->getFLCO() == FLCO_USER_USER) {
dmrLC->setFLCO(FLCO_GROUP);
LogMessage("DMR Slot %u, Rewrite inbound private call to %u to Group Call on TG 9 (BM reflector voice prompt)",slot,did);
return 9U;
} else {
return 0U;
}
} else if (m_bmAutoRewrite && did == 9U && m_dstRewriteID != 9U && m_dstRewriteID != 0U && (m_time + m_callHang) > currenttime && dmrLC->getFLCO() == FLCO_GROUP ) {
LogMessage("DMR Slot %u, Rewrite DST ID (TG) of outbound network traffic from %u to %u (return traffic during CallHang)",slot,did,m_dstRewriteID);
return m_dstRewriteID;
} else if (m_bmAutoRewrite && (did < 4000U || did > 5000U) && did > 0U && did !=9U && did < 99999U && dmrLC->getFLCO() == FLCO_USER_USER) {
m_dstRewriteID = did;
dmrLC->setFLCO(FLCO_GROUP);
LogMessage("DMR Slot %u, Rewrite outbound private call to %u Group Call (Connect talkgroup by private call)",slot,did);
return did;
} else if (m_bmAutoRewrite && (did < 4000U || did > 5000U) && did > 0U && did !=9U && did > 99999U) {
m_dstRewriteID = did;
}
return 0U;
}
void DMRAccessControl::setOverEndTime()
{
m_time = ::time(NULL);
}

View file

@ -1,4 +1,7 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016,2017 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
@ -16,56 +19,26 @@
#define DMRAccessControl_H
#include <vector>
#include <ctime>
#include "DMRLC.h"
class DMRAccessControl {
class CDMRAccessControl {
public:
static bool validateAccess(unsigned int srcId, unsigned int dstId, unsigned int slot, bool network);
static bool validateSrcId(unsigned int id);
static void init(const std::vector<unsigned int>& dstIdBlacklistSlot1RF, const std::vector<unsigned int>& dstIdWhitelistSlot1RF, const std::vector<unsigned int>& dstIdBlacklistSlot2RF, const std::vector<unsigned int>& dstIdWhitelistSlot2RF, const std::vector<unsigned int>& dstIdBlacklistSlot1NET, const std::vector<unsigned int>& dstIdWhitelistSlot1NET, const std::vector<unsigned int>& dstIdBlacklistSlot2NET, const std::vector<unsigned int>& dstIdWhitelistSlot2NET, const std::vector<unsigned int>& srcIdBlacklist, bool selfOnly, const std::vector<unsigned int>& prefixes,unsigned int id,unsigned int callHang, bool tgRewrteSlot1, bool tgRewrteSlot2, bool m_bmAutoRewrite, bool m_bmRewriteReflectorVoicePrompts);
static bool validateTGId(unsigned int slotNo, bool group, unsigned int id);
static void init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id);
static unsigned int dstIdRewrite(unsigned int id, unsigned int sid, unsigned int slot, bool network, CDMRLC* dmrLC);
static void setOverEndTime();
private:
static std::vector<unsigned int> m_dstBlackListSlot1RF;
static std::vector<unsigned int> m_dstBlackListSlot2RF;
static std::vector<unsigned int> m_dstWhiteListSlot1RF;
static std::vector<unsigned int> m_dstWhiteListSlot2RF;
static std::vector<unsigned int> m_dstBlackListSlot1NET;
static std::vector<unsigned int> m_dstBlackListSlot2NET;
static std::vector<unsigned int> m_dstWhiteListSlot1NET;
static std::vector<unsigned int> m_dstWhiteListSlot2NET;
static std::vector<unsigned int> m_srcIdBlacklist;
static std::vector<unsigned int> m_blackList;
static std::vector<unsigned int> m_whiteList;
static std::vector<unsigned int> m_prefixes;
static int m_callHang;
static std::vector<unsigned int> m_slot1TGWhiteList;
static std::vector<unsigned int> m_slot2TGWhiteList;
static bool m_selfOnly;
static unsigned int m_id;
static bool dstIdBlacklist(unsigned int did, unsigned int slot, bool network);
static bool dstIdWhitelist(unsigned int did, unsigned int slot, bool gt4k, bool network);
static bool validateSrcId(unsigned int id);
static time_t m_time;
static unsigned int m_dstRewriteID;
static unsigned int m_srcID;
static bool m_tgRewriteSlot1;
static bool m_tgRewriteSlot2;
static bool m_bmAutoRewrite;
static bool m_bmRewriteReflectorVoicePrompts;
static CDMRLC* m_lastdmrLC;
};
#endif

View file

@ -31,7 +31,9 @@ m_FID(0x00U),
m_GI(false),
m_bsId(0U),
m_srcId(0U),
m_dstId(0U)
m_dstId(0U),
m_dataContent(false),
m_CBF(0U)
{
m_data = new unsigned char[12U];
}
@ -64,39 +66,58 @@ bool CDMRCSBK::put(const unsigned char* bytes)
switch (m_CSBKO) {
case CSBKO_BSDWNACT:
m_GI = false;
m_bsId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
// CUtils::dump(1U, "Downlink Activate CSBK", m_data, 12U);
m_dataContent = false;
m_CBF = 0U;
CUtils::dump(1U, "Downlink Activate CSBK", m_data, 12U);
break;
case CSBKO_UUVREQ:
m_GI = false;
m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
// CUtils::dump(1U, "Unit to Unit Service Request CSBK", m_data, 12U);
m_dataContent = false;
m_CBF = 0U;
CUtils::dump(1U, "Unit to Unit Service Request CSBK", m_data, 12U);
break;
case CSBKO_UUANSRSP:
m_GI = false;
m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
// CUtils::dump(1U, "Unit to Unit Service Answer Response CSBK", m_data, 12U);
m_dataContent = false;
m_CBF = 0U;
CUtils::dump(1U, "Unit to Unit Service Answer Response CSBK", m_data, 12U);
break;
case CSBKO_PRECCSBK:
m_GI = (m_data[2U] & 0x40U) == 0x40U;
m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
// CUtils::dump(1U, "Preamble CSBK", m_data, 12U);
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
CUtils::dump(1U, "Preamble CSBK", m_data, 12U);
break;
case CSBKO_NACKRSP:
m_GI = false;
m_srcId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
m_dstId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
// CUtils::dump(1U, "Negative Acknowledge Response CSBK", m_data, 12U);
m_dataContent = false;
m_CBF = 0U;
CUtils::dump(1U, "Negative Acknowledge Response CSBK", m_data, 12U);
break;
default:
m_GI = false;
m_srcId = 0U;
m_dstId = 0U;
m_dataContent = false;
m_CBF = 0U;
CUtils::dump("Unhandled CSBK type", m_data, 12U);
return false;
return true;
}
return true;
@ -139,3 +160,13 @@ unsigned int CDMRCSBK::getDstId() const
{
return m_dstId;
}
bool CDMRCSBK::getDataContent() const
{
return m_dataContent;
}
unsigned char CDMRCSBK::getCBF() const
{
return m_CBF;
}

View file

@ -54,6 +54,9 @@ public:
unsigned int getSrcId() const;
unsigned int getDstId() const;
bool getDataContent() const;
unsigned char getCBF() const;
private:
unsigned char* m_data;
CSBKO m_CSBKO;
@ -62,6 +65,8 @@ private:
unsigned int m_bsId;
unsigned int m_srcId;
unsigned int m_dstId;
bool m_dataContent;
unsigned char m_CBF;
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 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
@ -12,6 +12,7 @@
*/
#include "DMRControl.h"
#include "DMRAccessControl.h"
#include "Defines.h"
#include "DMRCSBK.h"
#include "Log.h"
@ -20,23 +21,25 @@
#include <cassert>
#include <algorithm>
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blackList, const std::vector<unsigned int>& DstIdBlacklistSlot1RF, const std::vector<unsigned int>& DstIdWhitelistSlot1RF, const std::vector<unsigned int>& DstIdBlacklistSlot2RF, const std::vector<unsigned int>& DstIdWhitelistSlot2RF, const std::vector<unsigned int>& DstIdBlacklistSlot1NET, const std::vector<unsigned int>& DstIdWhitelistSlot1NET, const std::vector<unsigned int>& DstIdBlacklistSlot2NET, const std::vector<unsigned int>& DstIdWhitelistSlot2NET, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, int rssiMultiplier, int rssiOffset, unsigned int jitter, bool TGRewriteSlot1, bool TGRewriteSlot2, bool BMAutoRewrite, bool BMRewriteReflectorVoicePrompts) :
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter) :
m_id(id),
m_colorCode(colorCode),
m_selfOnly(selfOnly),
m_prefixes(prefixes),
m_blackList(blackList),
m_modem(modem),
m_network(network),
m_slot1(1U, timeout),
m_slot2(2U, timeout),
m_lookup(lookup)
{
assert(id != 0U);
assert(modem != NULL);
assert(display != NULL);
assert(lookup != NULL);
assert(rssi != NULL);
CDMRSlot::init(id, colorCode, callHang, selfOnly, prefixes, blackList, DstIdBlacklistSlot1RF, DstIdWhitelistSlot1RF, DstIdBlacklistSlot2RF, DstIdWhitelistSlot2RF, DstIdBlacklistSlot1NET, DstIdWhitelistSlot1NET, DstIdBlacklistSlot2NET, DstIdWhitelistSlot2NET, modem, network, display, duplex, m_lookup, rssiMultiplier, rssiOffset, jitter, TGRewriteSlot1, TGRewriteSlot2, BMAutoRewrite, BMRewriteReflectorVoicePrompts);
// Load black and white lists to DMRAccessControl
CDMRAccessControl::init(blacklist, whitelist, slot1TGWhitelist, slot2TGWhitelist, selfOnly, prefixes, id);
CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, display, duplex, m_lookup, rssi, jitter);
}
CDMRControl::~CDMRControl()
@ -65,29 +68,10 @@ bool CDMRControl::processWakeup(const unsigned char* data)
std::string src = m_lookup->find(srcId);
if (m_selfOnly) {
if (srcId != m_id) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;
}
} else {
if (std::find(m_blackList.begin(), m_blackList.end(), srcId) != m_blackList.end()) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;
}
unsigned int prefix = srcId / 10000U;
if (prefix == 0U || prefix > 999U) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;
}
if (m_prefixes.size() > 0U) {
if (std::find(m_prefixes.begin(), m_prefixes.end(), prefix) == m_prefixes.end()) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;
}
}
bool ret = CDMRAccessControl::validateSrcId(srcId);
if (!ret) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;
}
if (bsId == 0xFFFFFFU) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,7 @@
#if !defined(DMRControl_H)
#define DMRControl_H
#include "RSSIInterpolator.h"
#include "DMRNetwork.h"
#include "DMRLookup.h"
#include "Display.h"
@ -30,7 +31,7 @@
class CDMRControl {
public:
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blackList, const std::vector<unsigned int>& DstIdBlacklistSlot1RF, const std::vector<unsigned int>& DstIdWhitelistSlot1RF, const std::vector<unsigned int>& DstIdBlacklistSlot2RF, const std::vector<unsigned int>& DstIdWhitelistSlot2RF,const std::vector<unsigned int>& DstIdBlacklistSlot1NET, const std::vector<unsigned int>& DstIdWhitelistSlot1NET, const std::vector<unsigned int>& DstIdBlacklistSlot2NET, const std::vector<unsigned int>& DstIdWhitelistSlot2NET, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, int rssiMultiplier, int rssiOffset, unsigned int jitter, bool TGRewriteSlot1, bool TGRewriteSlot2, bool BMAutoRewrite, bool BMRewriteReflectorVoicePrompts);
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter);
~CDMRControl();
bool processWakeup(const unsigned char* data);
@ -44,16 +45,13 @@ public:
void clock();
private:
unsigned int m_id;
unsigned int m_colorCode;
bool m_selfOnly;
std::vector<unsigned int> m_prefixes;
std::vector<unsigned int> m_blackList;
CModem* m_modem;
CDMRNetwork* m_network;
CDMRSlot m_slot1;
CDMRSlot m_slot2;
CDMRLookup* m_lookup;
unsigned int m_id;
unsigned int m_colorCode;
CModem* m_modem;
CDMRNetwork* m_network;
CDMRSlot m_slot1;
CDMRSlot m_slot2;
CDMRLookup* m_lookup;
};
#endif

View file

@ -29,6 +29,8 @@
#include <cassert>
#include <cstring>
const unsigned char UDTF_NMEA = 0x05U;
CDMRDataHeader::CDMRDataHeader() :
m_data(NULL),
m_GI(false),
@ -78,13 +80,13 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
switch (dpf) {
case DPF_UNCONFIRMED_DATA:
CUtils::dump(1U, "Unconfirmed Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Unconfirmed Data Header", m_data, 12U);
m_F = (m_data[8U] & 0x80U) == 0x80U;
m_blocks = m_data[8U] & 0x7FU;
break;
case DPF_CONFIRMED_DATA:
CUtils::dump(1U, "Confirmed Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Confirmed Data Header", m_data, 12U);
m_F = (m_data[8U] & 0x80U) == 0x80U;
m_blocks = m_data[8U] & 0x7FU;
m_S = (m_data[9U] & 0x80U) == 0x80U;
@ -92,38 +94,46 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
break;
case DPF_RESPONSE:
CUtils::dump(1U, "Response Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Response Data Header", m_data, 12U);
m_blocks = m_data[8U] & 0x7FU;
break;
case DPF_PROPRIETARY:
CUtils::dump(1U, "Proprietary Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Proprietary Data Header", m_data, 12U);
break;
case DPF_DEFINED_RAW:
CUtils::dump(1U, "Raw or Status/Precoded Short Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Raw or Status/Precoded Short Data Header", m_data, 12U);
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU);
m_F = (m_data[8U] & 0x01U) == 0x01U;
m_S = (m_data[8U] & 0x02U) == 0x02U;
break;
case DPF_DEFINED_SHORT:
CUtils::dump(1U, "Defined Short Data Header", m_data, 12U);
CUtils::dump(1U, "DMR, Defined Short Data Header", m_data, 12U);
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU);
m_F = (m_data[8U] & 0x01U) == 0x01U;
m_S = (m_data[8U] & 0x02U) == 0x02U;
break;
case DPF_UDT:
CUtils::dump(1U, "Unified Data Transport Header", m_data, 12U);
CUtils::dump(1U, "DMR, Unified Data Transport Header", m_data, 12U);
m_blocks = m_data[8U] & 0x03U;
break;
default:
CUtils::dump("Unknown Data Header", m_data, 12U);
CUtils::dump("DMR, Unknown Data Header", m_data, 12U);
break;
}
if (dpf == DPF_UDT && m_blocks == 0U) {
unsigned char format = m_data[1U] & 0x0FU;
if (format == UDTF_NMEA) {
LogDebug("DMR, fixing broken Tytera MD-390 GPS data block count");
m_blocks = 3U;
}
}
return true;
}

View file

@ -114,8 +114,13 @@ const unsigned char FID_ETSI = 0U;
const unsigned char FID_DMRA = 16U;
enum FLCO {
FLCO_GROUP = 0,
FLCO_USER_USER = 3
FLCO_GROUP = 0,
FLCO_USER_USER = 3,
FLCO_TALKER_ALIAS_HEADER = 4,
FLCO_TALKER_ALIAS_BLOCK1 = 5,
FLCO_TALKER_ALIAS_BLOCK2 = 6,
FLCO_TALKER_ALIAS_BLOCK3 = 7,
FLCO_GPS_INFO = 8
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -16,7 +16,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DMREmbeddedLC.h"
#include "DMREmbeddedData.h"
#include "Hamming.h"
#include "Utils.h"
@ -26,20 +26,25 @@
#include <cassert>
#include <cstring>
CDMREmbeddedLC::CDMREmbeddedLC() :
m_rawLC(NULL),
m_state(LCS_NONE)
CDMREmbeddedData::CDMREmbeddedData() :
m_raw(NULL),
m_state(LCS_NONE),
m_data(NULL),
m_FLCO(FLCO_GROUP),
m_valid(false)
{
m_rawLC = new bool[128U];
m_raw = new bool[128U];
m_data = new bool[72U];
}
CDMREmbeddedLC::~CDMREmbeddedLC()
CDMREmbeddedData::~CDMREmbeddedData()
{
delete[] m_rawLC;
delete[] m_raw;
delete[] m_data;
}
// Add LC data (which may consist of 4 blocks) to the data store
CDMRLC* CDMREmbeddedLC::addData(const unsigned char* data, unsigned char lcss)
bool CDMREmbeddedData::addData(const unsigned char* data, unsigned char lcss)
{
assert(data != NULL);
@ -53,58 +58,70 @@ CDMRLC* CDMREmbeddedLC::addData(const unsigned char* data, unsigned char lcss)
// Is this the first block of a 4 block embedded LC ?
if (lcss == 1U) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a] = rawData[a + 4U];
m_raw[a] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_FIRST;
return NULL;
m_valid = false;
return false;
}
// Is this the 2nd block of a 4 block embedded LC ?
if (lcss == 3U && m_state == LCS_FIRST) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 32U] = rawData[a + 4U];
m_raw[a + 32U] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_SECOND;
return NULL;
return false;
}
// Is this the 3rd block of a 4 block embedded LC ?
if (lcss == 3U && m_state == LCS_SECOND) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 64U] = rawData[a + 4U];
m_raw[a + 64U] = rawData[a + 4U];
// Show we are ready for the final LC block
m_state = LCS_THIRD;
return NULL;
return false;
}
// Is this the final block of a 4 block embedded LC ?
if (lcss == 2U && m_state == LCS_THIRD) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 96U] = rawData[a + 4U];
m_raw[a + 96U] = rawData[a + 4U];
// Show that we're not ready for any more data
m_state = LCS_NONE;
// Process the complete data block
return processMultiBlockEmbeddedLC();
decodeEmbeddedData();
if (m_valid)
encodeEmbeddedData();
return m_valid;
}
// Is this a single block embedded LC
if (lcss == 0U) {
processSingleBlockEmbeddedLC(rawData + 4U);
return NULL;
}
return NULL;
return false;
}
void CDMREmbeddedLC::setData(const CDMRLC& lc)
void CDMREmbeddedData::setLC(const CDMRLC& lc)
{
lc.getData(m_data);
m_FLCO = lc.getFLCO();
m_valid = true;
encodeEmbeddedData();
}
void CDMREmbeddedData::encodeEmbeddedData()
{
bool lcData[72U];
lc.getData(lcData);
unsigned int crc;
CCRC::encodeFiveBit(lcData, crc);
CCRC::encodeFiveBit(m_data, crc);
bool data[128U];
::memset(data, 0x00U, 128U * sizeof(bool));
@ -117,19 +134,19 @@ void CDMREmbeddedLC::setData(const CDMRLC& lc)
unsigned int b = 0U;
for (unsigned int a = 0U; a < 11U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 16U; a < 27U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 32U; a < 42U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 48U; a < 58U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 64U; a < 74U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 80U; a < 90U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
for (unsigned int a = 96U; a < 106U; a++, b++)
data[a] = lcData[b];
data[a] = m_data[b];
// Hamming (16,11,4) check each row except the last one
for (unsigned int a = 0U; a < 112U; a += 16U)
@ -142,14 +159,14 @@ void CDMREmbeddedLC::setData(const CDMRLC& lc)
// The data is packed downwards in columns
b = 0U;
for (unsigned int a = 0U; a < 128U; a++) {
m_rawLC[a] = data[b];
m_raw[a] = data[b];
b += 16U;
if (b > 127U)
b -= 127U;
}
}
unsigned char CDMREmbeddedLC::getData(unsigned char* data, unsigned char n) const
unsigned char CDMREmbeddedData::getData(unsigned char* data, unsigned char n) const
{
assert(data != NULL);
@ -158,7 +175,7 @@ unsigned char CDMREmbeddedLC::getData(unsigned char* data, unsigned char n) cons
bool bits[40U];
::memset(bits, 0x00U, 40U * sizeof(bool));
::memcpy(bits + 4U, m_rawLC + n * 32U, 32U * sizeof(bool));
::memcpy(bits + 4U, m_raw + n * 32U, 32U * sizeof(bool));
unsigned char bytes[5U];
CUtils::bitsToByteBE(bits + 0U, bytes[0U]);
@ -193,7 +210,7 @@ unsigned char CDMREmbeddedLC::getData(unsigned char* data, unsigned char n) cons
}
// Unpack and error check an embedded LC
CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
void CDMREmbeddedData::decodeEmbeddedData()
{
// The data is unpacked downwards in columns
bool data[128U];
@ -201,7 +218,7 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
unsigned int b = 0U;
for (unsigned int a = 0U; a < 128U; a++) {
data[b] = m_rawLC[a];
data[b] = m_raw[a];
b += 16U;
if (b > 127U)
b -= 127U;
@ -210,33 +227,32 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
// Hamming (16,11,4) check each row except the last one
for (unsigned int a = 0U; a < 112U; a += 16U) {
if (!CHamming::decode16114(data + a))
return NULL;
return;
}
// Check the parity bits
for (unsigned int a = 0U; a < 16U; a++) {
bool parity = data[a + 0U] ^ data[a + 16U] ^ data[a + 32U] ^ data[a + 48U] ^ data[a + 64U] ^ data[a + 80U] ^ data[a + 96U] ^ data[a + 112U];
if (parity)
return NULL;
return;
}
// We have passed the Hamming check so extract the actual payload
bool lcData[72U];
b = 0U;
for (unsigned int a = 0U; a < 11U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 16U; a < 27U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 32U; a < 42U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 48U; a < 58U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 64U; a < 74U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 80U; a < 90U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 96U; a < 106U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
// Extract the 5 bit CRC
unsigned int crc = 0U;
@ -247,14 +263,60 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
if (data[106]) crc += 1U;
// Now CRC check this
if (!CCRC::checkFiveBit(lcData, crc))
if (!CCRC::checkFiveBit(m_data, crc))
return;
m_valid = true;
// Extract the FLCO
unsigned char flco;
CUtils::bitsToByteBE(m_data + 0U, flco);
m_FLCO = FLCO(flco & 0x3FU);
}
CDMRLC* CDMREmbeddedData::getLC() const
{
if (!m_valid)
return NULL;
return new CDMRLC(lcData);
if (m_FLCO != FLCO_GROUP && m_FLCO != FLCO_USER_USER)
return NULL;
return new CDMRLC(m_data);
}
// Deal with a single block embedded LC
void CDMREmbeddedLC::processSingleBlockEmbeddedLC(const bool* data)
bool CDMREmbeddedData::isValid() const
{
// Nothing interesting, or just NULL (I think)
return m_valid;
}
FLCO CDMREmbeddedData::getFLCO() const
{
return m_FLCO;
}
void CDMREmbeddedData::reset()
{
m_state = LCS_NONE;
m_valid = false;
}
bool CDMREmbeddedData::getRawData(unsigned char* data) const
{
assert(data != NULL);
if (!m_valid)
return false;
CUtils::bitsToByteBE(m_data + 0U, data[0U]);
CUtils::bitsToByteBE(m_data + 8U, data[1U]);
CUtils::bitsToByteBE(m_data + 16U, data[2U]);
CUtils::bitsToByteBE(m_data + 24U, data[3U]);
CUtils::bitsToByteBE(m_data + 32U, data[4U]);
CUtils::bitsToByteBE(m_data + 40U, data[5U]);
CUtils::bitsToByteBE(m_data + 48U, data[6U]);
CUtils::bitsToByteBE(m_data + 56U, data[7U]);
CUtils::bitsToByteBE(m_data + 65U, data[8U]);
return true;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -16,9 +16,10 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DMREmbeddedLC_H
#define DMREmbeddedLC_H
#ifndef DMREmbeddedData_H
#define DMREmbeddedData_H
#include "DMRDefines.h"
#include "DMRLC.h"
enum LC_STATE {
@ -28,23 +29,35 @@ enum LC_STATE {
LCS_THIRD
};
class CDMREmbeddedLC
class CDMREmbeddedData
{
public:
CDMREmbeddedLC();
~CDMREmbeddedLC();
CDMREmbeddedData();
~CDMREmbeddedData();
CDMRLC* addData(const unsigned char* data, unsigned char lcss);
bool addData(const unsigned char* data, unsigned char lcss);
CDMRLC* getLC() const;
void setLC(const CDMRLC& lc);
void setData(const CDMRLC& lc);
unsigned char getData(unsigned char* data, unsigned char n) const;
private:
bool* m_rawLC;
LC_STATE m_state;
bool getRawData(unsigned char* data) const;
CDMRLC* processMultiBlockEmbeddedLC();
void processSingleBlockEmbeddedLC(const bool* data);
bool isValid() const;
FLCO getFLCO() const;
void reset();
private:
bool* m_raw;
LC_STATE m_state;
bool* m_data;
FLCO m_FLCO;
bool m_valid;
void decodeEmbeddedData();
void encodeEmbeddedData();
};
#endif

View file

@ -90,7 +90,7 @@ then
fi
# Generate new file
curl 'http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0' 2>/dev/null | awk -F"," '{printf "%s\t%s\t%s\n", $1, $2, $4 == "" ? $3 : $4}' | sed -e 's/\(.\) .*/\1/g' > ${DMRIDFILE}
curl 'http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0' 2>/dev/null | sed -e 's/\t//g' | awk -F"," '/,/{gsub(/ /, "", $2); printf "%s\t%s\t%s\n", $1, $2, $3}' | sed -e 's/\(.\) .*/\1/g' > ${DMRIDFILE}
# Restart MMDVMHost
eval ${RESTARTCOMMAND}

99797
DMRIds.dat

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,7 +31,7 @@ const unsigned int BUFFER_LENGTH = 500U;
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, bool rssi, HW_TYPE hwType) :
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_address(),
m_port(port),
m_id(NULL),
@ -43,7 +43,6 @@ m_socket(local),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_rssi(rssi),
m_hwType(hwType),
m_status(WAITING_CONNECT),
m_retryTimer(1000U, 10U),
@ -52,6 +51,7 @@ m_buffer(NULL),
m_salt(NULL),
m_streamId(NULL),
m_rxData(1000U, "DMR Network"),
m_options(),
m_callsign(),
m_rxFrequency(0U),
m_txFrequency(0U),
@ -97,6 +97,11 @@ CDMRNetwork::~CDMRNetwork()
delete[] m_id;
}
void CDMRNetwork::setOptions(const std::string& options)
{
m_options = options;
}
void CDMRNetwork::setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url)
{
m_callsign = callsign;
@ -116,12 +121,8 @@ bool CDMRNetwork::open()
{
LogMessage("DMR, Opening DMR Network");
bool ret = m_socket.open();
if (!ret)
return false;
m_status = WAITING_LOGIN;
m_timeoutTimer.start();
m_status = WAITING_CONNECT;
m_timeoutTimer.stop();
m_retryTimer.start();
return true;
@ -266,10 +267,7 @@ bool CDMRNetwork::write(const CDMRData& data)
buffer[53U] = data.getBER();
if (m_rssi)
buffer[54U] = data.getRSSI();
else
buffer[54U] = 0x00U;
buffer[54U] = data.getRSSI();
if (m_debug)
CUtils::dump(1U, "Network Transmitted", buffer, HOMEBREW_DATA_PACKET_LENGTH);
@ -302,9 +300,17 @@ void CDMRNetwork::clock(unsigned int ms)
if (m_status == WAITING_CONNECT) {
m_retryTimer.clock(ms);
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
bool ret = open();
if (!ret)
m_retryTimer.start();
bool ret = m_socket.open();
if (ret) {
ret = writeLogin();
if (!ret)
return;
m_status = WAITING_LOGIN;
m_timeoutTimer.start();
}
m_retryTimer.start();
}
return;
@ -316,8 +322,7 @@ void CDMRNetwork::clock(unsigned int ms)
if (length < 0) {
LogError("DMR, Socket has failed, retrying connection to the master");
close();
m_status = WAITING_CONNECT;
m_retryTimer.start();
open();
return;
}
@ -346,26 +351,39 @@ void CDMRNetwork::clock(unsigned int ms)
We want it to reconnect so... */
LogError("DMR, Login to the master has failed, retrying ...");
close();
m_status = WAITING_CONNECT;
m_retryTimer.start();
open();
return;
}
} else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) {
switch (m_status) {
case WAITING_LOGIN:
::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t));
LogDebug("DMR, Sending authorisation");
::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t));
writeAuthorisation();
m_status = WAITING_AUTHORISATION;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_AUTHORISATION:
LogDebug("DMR, Sending configuration");
writeConfig();
m_status = WAITING_CONFIG;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_CONFIG:
if (m_options.empty()) {
LogMessage("DMR, Logged into the master successfully");
m_status = RUNNING;
} else {
LogDebug("DMR, Sending options");
writeOptions();
m_status = WAITING_OPTIONS;
}
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_OPTIONS:
LogMessage("DMR, Logged into the master successfully");
m_status = RUNNING;
m_timeoutTimer.start();
@ -377,8 +395,7 @@ void CDMRNetwork::clock(unsigned int ms)
} else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) {
LogError("DMR, Master is closing down");
close();
m_status = WAITING_CONNECT;
m_retryTimer.start();
open();
} else if (::memcmp(m_buffer, "MSTPONG", 7U) == 0) {
m_timeoutTimer.start();
} else if (::memcmp(m_buffer, "RPTSBKN", 7U) == 0) {
@ -397,6 +414,9 @@ void CDMRNetwork::clock(unsigned int ms)
case WAITING_AUTHORISATION:
writeAuthorisation();
break;
case WAITING_OPTIONS:
writeOptions();
break;
case WAITING_CONFIG:
writeConfig();
break;
@ -414,8 +434,7 @@ void CDMRNetwork::clock(unsigned int ms)
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
LogError("DMR, Connection to the master has timed out, retrying connection");
close();
m_status = WAITING_CONNECT;
m_retryTimer.start();
open();
}
}
@ -431,11 +450,11 @@ bool CDMRNetwork::writeLogin()
bool CDMRNetwork::writeAuthorisation()
{
unsigned int size = m_password.size();
size_t size = m_password.size();
unsigned char* in = new unsigned char[size + sizeof(uint32_t)];
::memcpy(in, m_salt, sizeof(uint32_t));
for (unsigned int i = 0U; i < size; i++)
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = m_password.at(i);
unsigned char out[40U];
@ -443,13 +462,24 @@ bool CDMRNetwork::writeAuthorisation()
::memcpy(out + 4U, m_id, 4U);
CSHA256 sha256;
sha256.buffer(in, size + sizeof(uint32_t), out + 8U);
sha256.buffer(in, (unsigned int)(size + sizeof(uint32_t)), out + 8U);
delete[] in;
return write(out, 40U);
}
bool CDMRNetwork::writeOptions()
{
char buffer[300U];
::memcpy(buffer + 0U, "RPTO", 4U);
::memcpy(buffer + 4U, m_id, 4U);
::strcpy(buffer + 8U, m_options.c_str());
return write((unsigned char*)buffer, (unsigned int)m_options.length() + 8U);
}
bool CDMRNetwork::writeConfig()
{
const char* software = "MMDVM";
@ -488,8 +518,16 @@ bool CDMRNetwork::writeConfig()
char longitude[20U];
::sprintf(longitude, "%09f", m_longitude);
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%.8s%.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
m_rxFrequency, m_txFrequency, m_power, m_colorCode, latitude, longitude, m_height, m_location.c_str(),
unsigned int power = m_power;
if (power > 99U)
power = 99U;
int height = m_height;
if (height > 999)
height = 999;
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(),
m_description.c_str(), slots, m_url.c_str(), m_version, software);
return write((unsigned char*)buffer, 302U);
@ -525,9 +563,8 @@ bool CDMRNetwork::write(const unsigned char* data, unsigned int length)
bool ret = m_socket.write(data, length, m_address, m_port);
if (!ret) {
LogError("DMR, Socket has failed when writing data to the master, retrying connection");
close();
m_status = WAITING_CONNECT;
m_retryTimer.start();
m_socket.close();
open();
return false;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,9 +31,11 @@
class CDMRNetwork
{
public:
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, bool rssi, HW_TYPE hwType);
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);
~CDMRNetwork();
void setOptions(const std::string& options);
void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url);
bool open();
@ -62,7 +64,6 @@ private:
bool m_enabled;
bool m_slot1;
bool m_slot2;
bool m_rssi;
HW_TYPE m_hwType;
enum STATUS {
@ -70,6 +71,7 @@ private:
WAITING_LOGIN,
WAITING_AUTHORISATION,
WAITING_CONFIG,
WAITING_OPTIONS,
RUNNING
};
@ -82,6 +84,8 @@ private:
CRingBuffer<unsigned char> m_rxData;
std::string m_options;
std::string m_callsign;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
@ -98,6 +102,7 @@ private:
bool writeLogin();
bool writeAuthorisation();
bool writeOptions();
bool writeConfig();
bool writePing();

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,7 +19,8 @@
#if !defined(DMRSlot_H)
#define DMRSlot_H
#include "DMREmbeddedLC.h"
#include "RSSIInterpolator.h"
#include "DMREmbeddedData.h"
#include "DMRDataHeader.h"
#include "DMRNetwork.h"
#include "RingBuffer.h"
@ -30,7 +31,6 @@
#include "DMRData.h"
#include "Display.h"
#include "Defines.h"
#include "DMREMB.h"
#include "Timer.h"
#include "Modem.h"
#include "DMRLC.h"
@ -50,15 +50,23 @@ public:
void clock();
static void init(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& SrcIdBlackList, const std::vector<unsigned int>& DstIdBlacklistSlot1RF, const std::vector<unsigned int>& DstIdWhitelistSlot1RF, const std::vector<unsigned int>& DstIdBlacklistSlot2RF, const std::vector<unsigned int>& DstIdWhitelistSlot2RF, const std::vector<unsigned int>& DstIdBlacklistSlot1NET, const std::vector<unsigned int>& DstIdWhitelistSlot1NET, const std::vector<unsigned int>& DstIdBlacklistSlot2NET, const std::vector<unsigned int>& DstIdWhitelistSlot2NET, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, int rssiMultiplier, int rssiOffset, unsigned int jitter, bool TGRewriteSlot1, bool TGRewriteSlot2, bool BMAutoRewrite, bool BMRewriteReflectorVoicePrompts);
static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter);
private:
unsigned int m_slotNo;
CRingBuffer<unsigned char> m_queue;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CDMREmbeddedLC m_rfEmbeddedLC;
CDMREmbeddedLC m_netEmbeddedLC;
CDMREmbeddedData m_rfEmbeddedLC;
CDMREmbeddedData* m_rfEmbeddedData;
unsigned int m_rfEmbeddedReadN;
unsigned int m_rfEmbeddedWriteN;
unsigned char m_rfTalkerId;
CDMREmbeddedData m_netEmbeddedLC;
CDMREmbeddedData* m_netEmbeddedData;
unsigned int m_netEmbeddedReadN;
unsigned int m_netEmbeddedWriteN;
unsigned char m_netTalkerId;
CDMRLC* m_rfLC;
CDMRLC* m_netLC;
CDMRDataHeader m_rfDataHeader;
@ -83,15 +91,17 @@ private:
unsigned int m_netErrs;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
CDMREMB m_lastEMB;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
static unsigned int m_id;
static unsigned int m_colorCode;
static bool m_selfOnly;
static std::vector<unsigned int> m_prefixes;
static std::vector<unsigned int> m_blackList;
static bool m_embeddedLCOnly;
static bool m_dumpTAData;
static CModem* m_modem;
static CDMRNetwork* m_network;
@ -100,8 +110,7 @@ private:
static CDMRLookup* m_lookup;
static unsigned int m_hangCount;
static int m_rssiMultiplier;
static int m_rssiOffset;
static CRSSIInterpolator* m_rssiMapper;
static unsigned int m_jitterTime;
static unsigned int m_jitterSlots;
@ -120,9 +129,6 @@ private:
void writeNetworkRF(const unsigned char* data, unsigned char dataType, unsigned char errors = 0U);
void writeNetworkRF(const unsigned char* data, unsigned char dataType, FLCO flco, unsigned int srcId, unsigned int dstId, unsigned char errors = 0U);
void endOfRFData();
void endOfNetData();
void writeEndRF(bool writeEnd = false);
void writeEndNet(bool writeEnd = false);

View file

@ -0,0 +1,32 @@
# DMRplus - Startup Options
## Introduction
This file is to give an overview over the Options-parameter in MMDVM.ini [DMR Network]-section.
## Example
You can pull some conection-info at startup to the DMRplus-Network to define the behavior of TS1 and TS2 in DMR-mode.
An example of such a line would be following:
Options=StartRef=4013;RelinkTime=15;UserLink=1;TS1_1=262;TS1_2=1;TS1_3=20;TS1_4=110;TS1_5=270;
If an option is set, it overwrites the setting preset at the master, if an option is empty, it unsets a predefined setting from
the master. If an option is not set, the default from the master would be taken over.
## What the parameters are about?
Here is a quick explaination about the options to be set:
* StartRef: This is the default reflector in TS2, in example: Refl. 4013
* RelinkTime: This is the time to fall back to the default-reflector if linked to another one and no local traffic is done,
not yet implemented, would come next
* UserLink: This defines, if users are allowed to link to another reflector (other than defined as startreflector)
* 1 = allow
* 0 = disallow
* TS1_1: This is the first of 5 talkgroups that could be set static, in example: TG262
* TS1_2: This is the second of 5 talkgroups that could be set static, in example: TG1
* TS1_3: This is the third of 5 talkgroups that could be set static, in example: TG20
* TS1_4: This is the fourth of 5 talkgroups that could be set static, in example: TG110
* TS1_5: This is the fifth of 5 talkgroups that could be set static, in example: TG270
---
Info created by DG9VH 2016-11-11

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 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
@ -36,10 +36,11 @@ bool CallsignCompare(const std::string& arg, const unsigned char* my)
// #define DUMP_DSTAR
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex) :
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool errorReply, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) :
m_callsign(NULL),
m_gateway(NULL),
m_selfOnly(selfOnly),
m_errorReply(errorReply),
m_blackList(blackList),
m_network(network),
m_display(display),
@ -58,6 +59,7 @@ m_rfTimeoutTimer(1000U, timeout),
m_netTimeoutTimer(1000U, timeout),
m_packetTimer(1000U, 0U, 300U),
m_ackTimer(1000U, 0U, 750U),
m_errTimer(1000U, 0U, 750U),
m_interval(),
m_elapsed(),
m_rfFrames(0U),
@ -70,9 +72,16 @@ m_rfErrs(0U),
m_netErrs(0U),
m_lastFrame(NULL),
m_lastFrameValid(false),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(rssiMapper != NULL);
m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
@ -111,18 +120,80 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned char type = data[0U];
if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) {
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
}
if (type == TAG_LOST && m_rfState == RS_RF_INVALID) {
m_rfState = RS_RF_LISTENING;
if (m_netState == RS_NET_IDLE) {
if (m_errorReply)
m_errTimer.start();
if (m_network != NULL)
m_network->reset();
}
return false;
}
if (type == TAG_LOST) {
m_rfState = RS_RF_LISTENING;
return false;
}
// Have we got RSSI bytes on the end of a D-Star header?
if (len == (DSTAR_HEADER_LENGTH_BYTES + 3U)) {
uint16_t raw = 0U;
raw |= (data[42U] << 8) & 0xFF00U;
raw |= (data[43U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
// Have we got RSSI bytes on the end of D-Star data?
if (len == (DSTAR_FRAME_LENGTH_BYTES + 3U)) {
uint16_t raw = 0U;
raw |= (data[13U] << 8) & 0xFF00U;
raw |= (data[14U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
if (type == TAG_HEADER) {
CDStarHeader header(data + 1U);
m_rfHeader = header;
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header.getMyCall1(my1);
@ -130,7 +201,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
// Is this a transmission destined for a repeater?
if (!header.isRepeater()) {
LogMessage("D-Star, non repeater RF header received from %8.8s", my1);
m_rfState = RS_RF_REJECTED;
m_rfState = RS_RF_INVALID;
return false;
}
@ -140,7 +211,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1);
m_rfState = RS_RF_REJECTED;
m_rfState = RS_RF_INVALID;
return false;
}
@ -171,9 +242,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (!m_rfTimeoutTimer.isRunning())
m_rfTimeoutTimer.start();
m_rfHeader = header;
m_ackTimer.stop();
m_errTimer.stop();
m_rfBits = 1U;
m_rfErrs = 0U;
@ -181,6 +251,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfFrames = 1U;
m_rfN = 0U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
// Modify the header
header.setRepeater(false);
@ -203,13 +278,27 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfState = RS_RF_AUDIO;
if (m_netState == RS_NET_IDLE)
if (m_netState == RS_NET_IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
}
LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your);
} else if (type == TAG_EOT) {
if (m_rfState == RS_RF_REJECTED) {
m_rfState = RS_RF_LISTENING;
} else if (m_rfState == RS_RF_INVALID) {
m_rfState = RS_RF_LISTENING;
if (m_netState == RS_NET_IDLE) {
if (m_errorReply)
m_errTimer.start();
if (m_network != NULL)
m_network->reset();
}
return false;
} else if (m_rfState == RS_RF_AUDIO) {
if (m_net)
writeNetworkDataRF(DSTAR_END_PATTERN_BYTES, 0U, true);
@ -217,7 +306,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (m_duplex)
writeQueueEOTRF();
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
}
@ -226,6 +318,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
} else if (type == TAG_DATA) {
if (m_rfState == RS_RF_REJECTED) {
return false;
} else if (m_rfState == RS_RF_INVALID) {
return false;
} else if (m_rfState == RS_RF_LISTENING) {
// The sync is regenerated by the modem so can do exact match
if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) {
@ -236,23 +330,25 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
return false;
} else if (m_rfState == RS_RF_AUDIO) {
unsigned int errors = 0U;
if (!m_rfHeader.isDataPacket())
if (!m_rfHeader.isDataPacket()) {
errors = m_fec.regenerateDStar(data + 1U);
m_display->writeDStarBER(float(errors) / 0.48F);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_rfErrs += errors;
}
m_rfErrs += errors;
m_rfBits += 48U;
m_rfFrames++;
// The sync is regenerated by the modem so can do exact match
if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0)
m_rfN = 0U;
// Regenerate the sync
if (m_rfN == 0U)
// Regenerate the sync and send the RSSI data to the display
if (m_rfN == 0U) {
CSync::addDStarSync(data + 1U);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48", m_rfN, errors);
m_display->writeDStarRSSI(m_rssi);
}
if (m_net)
writeNetworkDataRF(data, errors, false);
@ -274,8 +370,15 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (header == NULL)
return false;
m_rfHeader = *header;
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header->getMyCall1(my1);
// Is this a transmission destined for a repeater?
if (!header->isRepeater()) {
LogMessage("D-Star, non repeater RF header received from %8.8s", my1);
m_rfState = RS_RF_INVALID;
delete header;
return false;
}
@ -285,19 +388,22 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1);
m_rfState = RS_RF_INVALID;
delete header;
return false;
}
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header->getMyCall1(my1);
if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RS_RF_REJECTED;
delete header;
return false;
}
if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RS_RF_REJECTED;
delete header;
return false;
}
@ -319,8 +425,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
// Create a dummy start frame to replace the received frame
m_ackTimer.stop();
m_rfHeader = *header;
m_errTimer.stop();
m_rfBits = 1U;
m_rfErrs = 0U;
@ -328,6 +433,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfN = 0U;
m_rfFrames = 1U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U];
start[0U] = TAG_HEADER;
@ -357,14 +467,14 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
delete header;
unsigned int errors = 0U;
if (!m_rfHeader.isDataPacket())
if (!m_rfHeader.isDataPacket()) {
errors = m_fec.regenerateDStar(data + 1U);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_rfErrs += errors;
}
m_rfErrs += errors;
m_rfBits += 48U;
LogDebug("D-Star, audio sequence no. %u, errs: %u/48", m_rfN, errors);
if (m_net)
writeNetworkDataRF(data, errors, false);
@ -377,8 +487,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfN = (m_rfN + 1U) % 21U;
if (m_netState == RS_NET_IDLE)
if (m_netState == RS_NET_IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
m_display->writeDStarBER(float(errors) / 0.48F);
}
LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your);
}
@ -476,6 +589,7 @@ void CDStarControl::writeNetwork()
m_packetTimer.start();
//m_elapsed.start(); // commented out and placed lower down due to delay introduced somewhere below here.
m_ackTimer.stop();
m_errTimer.stop();
m_lastFrameValid = false;
@ -551,8 +665,6 @@ void CDStarControl::writeNetwork()
m_netN = n;
LogDebug("D-Star, audio sequence no. %u, errs: %u/48", m_netN, errors);
// Regenerate the sync
if (n == 0U)
CSync::addDStarSync(data + 2U);
@ -583,6 +695,12 @@ void CDStarControl::clock()
m_ackTimer.stop();
}
m_errTimer.clock(ms);
if (m_errTimer.isRunning() && m_errTimer.hasExpired()) {
sendError();
m_errTimer.stop();
}
m_rfTimeoutTimer.clock(ms);
m_netTimeoutTimer.clock(ms);
@ -932,3 +1050,45 @@ void CDStarControl::sendAck()
writeQueueEOTRF();
}
void CDStarControl::sendError()
{
unsigned char user[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getMyCall1(user);
CDStarHeader header;
header.setUnavailable(true);
header.setMyCall1(m_callsign);
header.setYourCall(user);
header.setRPTCall1(m_callsign);
header.setRPTCall2(m_callsign);
unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 1U];
header.get(data + 1U);
data[0U] = TAG_HEADER;
writeQueueHeaderRF(data);
writeQueueDataRF(DSTAR_NULL_FRAME_SYNC_BYTES);
LINK_STATUS status = LS_NONE;
unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH];
if (m_network != NULL)
m_network->getStatus(status, reflector);
char text[40U];
if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_LINKED_LOOPBACK)
::sprintf(text, "%-8.8s BER: %.1f%% ", reflector, float(m_rfErrs * 100U) / float(m_rfBits));
else
::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits));
m_slowData.setText(text);
::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U);
for (unsigned int i = 0U; i < 19U; i++) {
m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES);
writeQueueDataRF(data);
}
writeQueueEOTRF();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,7 @@
#if !defined(DStarControl_H)
#define DStarControl_H
#include "RSSIInterpolator.h"
#include "DStarNetwork.h"
#include "DStarSlowData.h"
#include "DStarDefines.h"
@ -36,7 +37,7 @@
class CDStarControl {
public:
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex);
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool errorReply, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper);
~CDStarControl();
bool writeModem(unsigned char* data, unsigned int len);
@ -49,6 +50,7 @@ private:
unsigned char* m_callsign;
unsigned char* m_gateway;
bool m_selfOnly;
bool m_errorReply;
std::vector<std::string> m_blackList;
CDStarNetwork* m_network;
CDisplay* m_display;
@ -67,6 +69,7 @@ private:
CTimer m_netTimeoutTimer;
CTimer m_packetTimer;
CTimer m_ackTimer;
CTimer m_errTimer;
CStopWatch m_interval;
CStopWatch m_elapsed;
unsigned int m_rfFrames;
@ -79,6 +82,12 @@ private:
unsigned int m_netErrs;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeNetwork();
@ -105,6 +114,7 @@ private:
void blankDTMF(unsigned char* data) const;
void sendAck();
void sendError();
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -44,7 +44,8 @@ enum RPT_RF_STATE {
RS_RF_LATE_ENTRY,
RS_RF_AUDIO,
RS_RF_DATA,
RS_RF_REJECTED
RS_RF_REJECTED,
RS_RF_INVALID
};
enum RPT_NET_STATE {

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -84,6 +84,17 @@ void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, co
writeDStarInt(my1, my2, your, type, reflector);
}
void CDisplay::writeDStarRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeDStarRSSIInt(rssi);
}
void CDisplay::writeDStarBER(float ber)
{
writeDStarBERInt(ber);
}
void CDisplay::clearDStar()
{
if (m_timer1.hasExpired()) {
@ -110,6 +121,17 @@ void CDisplay::writeDMR(unsigned int slotNo, const std::string& src, bool group,
writeDMRInt(slotNo, src, group, dst, type);
}
void CDisplay::writeDMRRSSI(unsigned int slotNo, unsigned char rssi)
{
if (rssi != 0U)
writeDMRRSSIInt(slotNo, rssi);
}
void CDisplay::writeDMRBER(unsigned int slotNo, float ber)
{
writeDMRBERInt(slotNo, ber);
}
void CDisplay::clearDMR(unsigned int slotNo)
{
if (slotNo == 1U) {
@ -144,6 +166,17 @@ void CDisplay::writeFusion(const char* source, const char* dest, const char* typ
writeFusionInt(source, dest, type, origin);
}
void CDisplay::writeFusionRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeFusionRSSIInt(rssi);
}
void CDisplay::writeFusionBER(float ber)
{
writeFusionBERInt(ber);
}
void CDisplay::clearFusion()
{
if (m_timer1.hasExpired()) {
@ -166,6 +199,17 @@ void CDisplay::writeP25(const char* source, bool group, unsigned int dest, const
writeP25Int(source, group, dest, type);
}
void CDisplay::writeP25RSSI(unsigned char rssi)
{
if (rssi != 0U)
writeP25RSSIInt(rssi);
}
void CDisplay::writeP25BER(float ber)
{
writeP25BERInt(ber);
}
void CDisplay::clearP25()
{
if (m_timer1.hasExpired()) {
@ -236,3 +280,35 @@ void CDisplay::clock(unsigned int ms)
void CDisplay::clockInt(unsigned int ms)
{
}
void CDisplay::writeDStarRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeDStarBERInt(float ber)
{
}
void CDisplay::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
}
void CDisplay::writeDMRBERInt(unsigned int slotNo, float ber)
{
}
void CDisplay::writeFusionRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeFusionBERInt(float ber)
{
}
void CDisplay::writeP25RSSIInt(unsigned char rssi)
{
}
void CDisplay::writeP25BERInt(float ber)
{
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -36,15 +36,23 @@ public:
void setError(const char* text);
void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
void writeDStarRSSI(unsigned char rssi);
void writeDStarBER(float ber);
void clearDStar();
void writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
void writeDMRRSSI(unsigned int slotNo, unsigned char rssi);
void writeDMRBER(unsigned int slotNo, float ber);
void clearDMR(unsigned int slotNo);
void writeFusion(const char* source, const char* dest, const char* type, const char* origin);
void writeFusionRSSI(unsigned char rssi);
void writeFusionBER(float ber);
void clearFusion();
void writeP25(const char* source, bool group, unsigned int dest, const char* type);
void writeP25RSSI(unsigned char rssi);
void writeP25BER(float ber);
void clearP25();
void writeCW();
@ -60,15 +68,23 @@ protected:
virtual void setErrorInt(const char* text) = 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);
virtual void writeDStarBERInt(float ber);
virtual void clearDStarInt() = 0;
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0;
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void writeDMRBERInt(unsigned int slotNo, float ber);
virtual void clearDMRInt(unsigned int slotNo) = 0;
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin) = 0;
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void writeFusionBERInt(float ber);
virtual void clearFusionInt() = 0;
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type) = 0;
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void writeP25BERInt(float ber);
virtual void clearP25Int() = 0;
virtual void writeCWInt() = 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Jonathan Naylor G4KLX & Tony Corbett G0WFV
*
* 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
@ -36,6 +36,11 @@ char m_buffer2[128U];
char m_buffer3[128U];
char m_buffer4[128U];
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
CHD44780::CHD44780(unsigned int rows, unsigned int cols, const std::string& callsign, unsigned int dmrid, const std::vector<unsigned int>& pins, unsigned int i2cAddress, bool pwm, unsigned int pwmPin, unsigned int pwmBright, unsigned int pwmDim, bool displayClock, bool utc, bool duplex) :
CDisplay(),
m_rows(rows),
@ -59,12 +64,9 @@ m_duplex(duplex),
//m_duplex(true), // uncomment to force duplex display for testing!
m_fd(-1),
m_dmr(false),
m_clockDisplayTimer(1000U, 0U, 250U) // Update the clock display every 250ms
/*
m_dmrScrollTimer1(1000U, 0U, 250U), // Scroll speed for slot 1 - every 250ms
m_dmrScrollTimer2(1000U, 0U, 250U), // Scroll speed for slot 2 - every 250ms
m_dstarScrollTimer(1000U, 0U, 250U) // Scroll speed for D-Star - every 250ms
*/
m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms
m_rssiCount1(0U),
m_rssiCount2(0U)
{
assert(rows > 1U);
assert(cols > 15U);
@ -290,8 +292,6 @@ void CHD44780::pcf8574LCDSetup()
void CHD44780::setIdleInt()
{
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
::lcdClear(m_fd);
@ -330,8 +330,6 @@ void CHD44780::setErrorInt(const char* text)
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdClear(m_fd);
if (m_pwm) {
@ -357,8 +355,6 @@ void CHD44780::setLockoutInt()
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdClear(m_fd);
if (m_pwm) {
@ -439,14 +435,20 @@ void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your,
::lcdPutchar(m_fd, 1);
::lcdPrintf(m_fd, " %.*s", m_cols, m_buffer1);
// Start the D-Star scroll timer if text in m_buffer1 will not fit in the space available
/*if (strlen(m_buffer1) > m_cols) {
::sprintf(m_buffer3, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer1, m_buffer3);
m_dstarScrollTimer.start();
}*/
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearDStarInt()
@ -456,7 +458,6 @@ void CHD44780::clearDStarInt()
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dstarScrollTimer.stop();
::lcdClear(m_fd);
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
@ -538,13 +539,6 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
::sprintf(m_buffer1, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer1);
// Start the DMR scroll timer on slot 1 if text in m_buffer1 will not fit in the space available
/*if (strlen(m_buffer1) > m_cols - 5 ) {
::sprintf(m_buffer3, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer1, m_buffer3);
m_dmrScrollTimer1.start();
}*/
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2) - 1);
::lcdPuts(m_fd, " ");
@ -568,13 +562,6 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
::sprintf(m_buffer2, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer2);
// Start the DMR scroll timer on slot 2 if text in m_buffer2 will not fit in the space available
/*if (strlen(m_buffer2) > m_cols - 5 ) {
::sprintf(m_buffer4, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer2, m_buffer4);
m_dmrScrollTimer2.start();
}*/
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2));
::lcdPuts(m_fd, " ");
@ -622,6 +609,33 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
}
}
m_dmr = true;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CHD44780::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (m_rows > 2) {
if (slotNo == 1U) {
if (m_rssiCount1 == 0U) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U) {
::lcdPosition(m_fd, (m_cols / 2), 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
}
void CHD44780::clearDMRInt(unsigned int slotNo)
@ -634,16 +648,23 @@ void CHD44780::clearDMRInt(unsigned int slotNo)
if (m_duplex) {
if (slotNo == 1U) {
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPrintf(m_fd, "1 %.*s", m_cols - 2U, LISTENING);
if (m_rows > 2) { // clear slot 1 RSSI
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE);
}
} else {
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "2 %.*s", m_cols - 2U, LISTENING);
if (m_rows > 2) { // cleat slot 2 RSSI
::lcdPosition(m_fd, m_cols / 2, 3);
::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE);
}
}
} else {
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
@ -715,6 +736,19 @@ void CHD44780::writeFusionInt(const char* source, const char* dest, const char*
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearFusionInt()
@ -734,12 +768,18 @@ void CHD44780::clearFusionInt()
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 4U && m_cols == 20U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 2 && m_cols == 40U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
@ -800,6 +840,19 @@ void CHD44780::writeP25Int(const char* source, bool group, unsigned int dest, co
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearP25Int()
@ -819,12 +872,18 @@ void CHD44780::clearP25Int()
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 4U && m_cols == 20U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 2 && m_cols == 40U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
@ -846,9 +905,6 @@ void CHD44780::clearCWInt()
void CHD44780::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
//m_dmrScrollTimer1.clock(ms);
//m_dmrScrollTimer2.clock(ms);
//m_dstarScrollTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
@ -878,35 +934,6 @@ void CHD44780::clockInt(unsigned int ms)
m_clockDisplayTimer.start();
}
/* Scrolling disabled for now as it is slowing things down just enough to screw with the audio processing!
// DMR Slot 1 scrolling
if (m_dmrScrollTimer1.isRunning() && m_dmrScrollTimer1.hasExpired()) {
strncat(m_buffer1, m_buffer1, 1); // Move the first character to the end of the buffer
memmove(m_buffer1, m_buffer1 + 1, strlen(m_buffer1)); // Strip the first character
::lcdPosition(m_fd, 2, (m_rows / 2) - 1); // Position on the LCD
::lcdPrintf(m_fd, "%.*s", m_cols - 5U, m_buffer1); // Print it out
m_dmrScrollTimer1.start(); // Restart the scroll timer
}
// DMR Slot 2 scrolling
if (m_dmrScrollTimer2.isRunning() && m_dmrScrollTimer2.hasExpired()) {
strncat(m_buffer2, m_buffer2, 1);
memmove(m_buffer2, m_buffer2 + 1, strlen(m_buffer2));
::lcdPosition(m_fd, 2, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols - 5U, m_buffer2);
m_dmrScrollTimer2.start();
}
// D-Star scrolling
if (m_dstarScrollTimer.isRunning() && m_dstarScrollTimer.hasExpired()) {
strncat(m_buffer1, m_buffer1, 1);
memmove(m_buffer1, m_buffer1 + 1, strlen(m_buffer1));
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
m_dstarScrollTimer.start();
}*/
}
void CHD44780::close()

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Jonathan Naylor G4KLX & Tony Corbett G0WFV
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -102,16 +102,20 @@ protected:
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
@ -140,6 +144,8 @@ private:
int m_fd;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
/*
CTimer m_dmrScrollTimer1;
CTimer m_dmrScrollTimer2;

View file

@ -1,9 +0,0 @@
G0WFV's HD44780 todo list ...
As I only have a 2x16 screen at the moment, development has been done based
on this size screen. Where it is simple, I have attempted ***but not tested***
to implement functions for larger screens.
Here's a list of things I would like to accomplish in the near future ...
- Nothing planned at this time! Any suggestions?

10
ISSUES.txt Normal file
View file

@ -0,0 +1,10 @@
D-Star: No obvious issues.
DMR: There is an issue where transmitted data (text messages) isnt picked up as reliably from an MMDVM than (say) a Hytera repeater. In order to address this, I need to see a trace from a Hytera repeater transmitting text, both from cold (no tx) and when already running. The DMRRX receiver from the DV4RX code in my GitHub repository would do the job, provided you have a DV4mini and a repeater within range.
YSF: There are a number of issues that need addressing:
1. At the end of a transmission, my radio does a nice bleep when its listening to a DR-1X but not when receiving from my MMDVM. I dont know why. I think the YSFRX from DV4RX may be able to give the answer.
2. The data sent to aprs.fi from southern hemisphere users isnt always correct. Maybe the GPS format used by YSF still has some secrets to reveal.
3. The last page of Wires-X data doesnt display. A trace of a complete listing from a “real” Wires-X system, right down to the last page would be useful. Yet again the YSFRX would be useful here.
P25: No obvious issues.

733
LCDproc.cpp Normal file
View file

@ -0,0 +1,733 @@
/*
* Copyright (C) 2016, 2017 by Tony Corbett G0WFV
*
* 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.
*/
/*
* Some LCD displays include additional LEDs for status.
* If they exist, the LDCproc server will use the output command.
* If the LEDs do not exist, the command is ignored.
* to control these LEDs Below are the values for the Crystalfontz CFA-635.
* N4IRS
* LED 1 (DMR)
* Green 1 0000 0001
* Red 16 0001 0000
* Yellow 17 0001 0001
* LED 2 (P25)
* Green 2 0000 0010
* Red 32 0010 0000
* Yellow 34 0010 0010
* LED 3 (Fusion)
* Green 4 0000 0100
* Red 64 0100 0000
* Yellow 68 1000 0100
* LED 4 (D-Star)
* Green 8 0000 1000
* Red 128 1000 0000
* Yellow 136 1000 1000
*/
#include "LCDproc.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <clocale>
#include <ctime>
#if !defined(_WIN32) && !defined(_WIN64)
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#else
#include <winsock.h>
#endif
#define BUFFER_MAX_LEN 128
int m_socketfd;
char m_buffer[BUFFER_MAX_LEN];
fd_set m_readfds, m_writefds;
struct timeval m_timeout;
int m_recvsize;
unsigned int m_rows(0);
unsigned int m_cols(0);
bool m_screensDefined(false);
bool m_connected(false);
char m_displayBuffer1[BUFFER_MAX_LEN];
char m_displayBuffer2[BUFFER_MAX_LEN];
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) :
CDisplay(),
m_address(address),
m_port(port),
m_localPort(localPort),
m_callsign(callsign),
m_dmrid(dmrid),
m_displayClock(displayClock),
m_utc(utc),
m_duplex(duplex),
//m_duplex(true), // uncomment to force duplex display for testing!
m_dimOnIdle(dimOnIdle),
m_dmr(false),
m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms
m_rssiCount1(0U),
m_rssiCount2(0U)
{
}
CLCDproc::~CLCDproc()
{
}
bool CLCDproc::open()
{
const char *server;
unsigned int port, localPort;
struct sockaddr_in serverAddress, clientAddress;
struct hostent *h;
server = m_address.c_str();
port = m_port;
localPort = m_localPort;
/* Create TCP socket */
m_socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (m_socketfd == -1) {
LogError("LCDproc, failed to create socket");
return false;
}
/* Sets client address (random port - need to specify manual port from ini file?) */
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
//clientAddress.sin_port = htons(0);
clientAddress.sin_port = htons(localPort);
/* Bind the address to the socket */
if (bind(m_socketfd, (struct sockaddr *)&clientAddress, sizeof(clientAddress)) == -1) {
LogError("LCDproc, error whilst binding address");
return false;
}
/* Lookup the hostname address */
h = gethostbyname(server);
/* Sets server address */
serverAddress.sin_family = h->h_addrtype;
memcpy((char*)&serverAddress.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
serverAddress.sin_port = htons(port);
if (connect(m_socketfd, (struct sockaddr * )&serverAddress, sizeof(serverAddress))==-1) {
LogError("LCDproc, cannot connect to server");
return false;
}
socketPrintf(m_socketfd, "hello"); // Login to the LCD server
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
return true;
}
void CLCDproc::setIdleInt()
{
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
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, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setErrorInt(const char* text)
{
assert(text != NULL);
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, "widget_set Status Status %u %u Error", m_cols - 4, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setLockoutInt()
{
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, "widget_set Status Status %u %u Lockout", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
// LED 4 Green 8 Red 128 Yellow 136
void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
assert(my2 != NULL);
assert(your != NULL);
assert(type != NULL);
assert(reflector != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DStar -priority foreground");
socketPrintf(m_socketfd, "widget_set DStar Mode 1 1 \"D-Star\"");
::sprintf(m_displayBuffer1, "%.8s", your);
char *p = m_displayBuffer1;
for (; *p; ++p) {
if (*p == ' ')
*p = '_';
}
if (strcmp(reflector, " ") != 0)
sprintf(m_displayBuffer2, " via %.8s", reflector);
else
memset(m_displayBuffer2, 0, BUFFER_MAX_LEN);
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s to %s%s\"", m_cols - 1, my1, my2, m_displayBuffer1, m_displayBuffer2);
} else {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s\"", m_cols - 1, my1, my2);
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, m_displayBuffer1, m_displayBuffer2);
socketPrintf(m_socketfd, "output 128"); // Set LED4 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearDStarInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 8"); // Set LED4 color green
}
// LED 1 Green 1 Red 16 Yellow 17
void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != NULL);
if (!m_dmr) {
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DMR -priority foreground");
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
else
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u \"\"", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u \"\"", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 %u %u %u h 3 \"\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
}
}
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2, m_cols - 1, m_rows / 2, src.c_str(), group ? "TG" : "", dst.c_str());
else
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s > %s%s\"", m_cols - 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s >\"", m_cols - 1, src.c_str());
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, group ? "TG" : "", dst.c_str());
}
}
socketPrintf(m_socketfd, "output 16"); // Set LED1 color red
m_dmr = true;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CLCDproc::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (m_rows > 2) {
if (slotNo == 1U) {
if (m_rssiCount1 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u -%3udBm", 1, 4, rssi);
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u -%3udBm", (m_cols / 2) + 1, 4, rssi);
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
}
void CLCDproc::clearDMRInt(unsigned int slotNo)
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_duplex) {
if (slotNo == 1U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u %*.s", 1, 4, m_cols / 2, " ");
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
socketPrintf(m_socketfd, "output 1"); // Set LED1 color green
}
// LED 3 Green 4 Red 64 Yellow 68
void CLCDproc::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
assert(source != NULL);
assert(dest != NULL);
assert(type != NULL);
assert(origin != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set YSF -priority foreground");
socketPrintf(m_socketfd, "widget_set YSF Mode 1 1 \"System Fusion\"");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, dest);
} else {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"%s%u\"", dest);
socketPrintf(m_socketfd, "output 64"); // Set LED3 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearFusionInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 4"); // Set LED3 color green
}
// LED 2 Green 2 Red 32 Yellow 34
void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != NULL);
assert(type != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set P25 -priority foreground");
socketPrintf(m_socketfd, "widget_set P25 Mode 1 1 P25");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest);
} else {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest);
socketPrintf(m_socketfd, "output 32"); // Set LED2 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearP25Int()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set P25 Line3 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 2"); // Set LED2 color green
}
void CLCDproc::writeCWInt()
{
}
void CLCDproc::clearCWInt()
{
}
void CLCDproc::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
time_t currentTime;
struct tm *Time;
time(&currentTime);
if (m_utc)
Time = gmtime(&currentTime);
else
Time = localtime(&currentTime);
setlocale(LC_TIME, "");
strftime(m_displayBuffer1, 128, "%X", Time); // Time
strftime(m_displayBuffer2, 128, "%x", Time); // Date
if (m_cols < 26U && m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set Status Time %u 2 \"%s%s\"", m_cols - 9, strlen(m_displayBuffer1) > 8 ? "" : " ", m_displayBuffer1);
} else {
socketPrintf(m_socketfd, "widget_set Status Time %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2, m_displayBuffer1);
socketPrintf(m_socketfd, "widget_set Status Date %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2 + 1, m_displayBuffer2);
}
m_clockDisplayTimer.start();
}
// We must set all this information on each select we do
FD_ZERO(&m_readfds); // empty readfds
// Then we put all the descriptors we want to wait for in a mask = m_readfds
FD_SET(m_socketfd, &m_readfds);
// Timeout, we will stop waiting for information
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
/* The first parameter is the biggest descriptor + 1. The first one was 0, so
* every other descriptor will be bigger
*
* readfds = &m_readfds
* writefds = we are not waiting for writefds
* exceptfds = we are not waiting for exception fds
*/
if (select(m_socketfd + 1, &m_readfds, NULL, NULL, &m_timeout) == -1)
LogError("LCDproc, error on select");
// If something was received from the server...
if (FD_ISSET(m_socketfd, &m_readfds)) {
m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0);
if (m_recvsize == -1)
LogError("LCDproc, cannot receive information");
m_buffer[m_recvsize] = '\0';
char *argv[256];
size_t len = strlen(m_buffer);
// Now split the string into tokens...
int argc = 0;
int newtoken = 1;
for (size_t i = 0U; i < len; i++) {
switch (m_buffer[i]) {
case ' ':
newtoken = 1;
m_buffer[i] = 0;
break;
default: /* regular chars, keep tokenizing */
if (newtoken)
argv[argc++] = m_buffer + i;
newtoken = 0;
break;
case '\0':
case '\n':
m_buffer[i] = 0;
if (argc > 0) {
if (0 == strcmp(argv[0], "listen")) {
LogDebug("LCDproc, the %s screen is displayed", argv[1]);
} else if (0 == strcmp(argv[0], "ignore")) {
LogDebug("LCDproc, the %s screen is hidden", argv[1]);
} else if (0 == strcmp(argv[0], "key")) {
LogDebug("LCDproc, Key %s", argv[1]);
} else if (0 == strcmp(argv[0], "menu")) {
} else if (0 == strcmp(argv[0], "connect")) {
// connect LCDproc 0.5.7 protocol 0.3 lcd wid 16 hgt 2 cellwid 5 cellhgt 8
int a;
for (a = 1; a < argc; a++) {
if (0 == strcmp(argv[a], "wid"))
m_cols = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "hgt"))
m_rows = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "cellwid")) {
//lcd_cellwid = atoi(argv[++a]);
} else if (0 == strcmp(argv[a], "cellhgt")) {
//lcd_cellhgt = atoi(argv[++a]);
}
}
m_connected = true;
socketPrintf(m_socketfd, "client_set -name MMDVMHost");
} else if (0 == strcmp(argv[0], "bye")) {
//close the socket- todo
} else if (0 == strcmp(argv[0], "success")) {
//LogDebug("LCDproc, command successful");
} else if (0 == strcmp(argv[0], "huh?")) {
sprintf(m_displayBuffer1, "LCDproc, command failed:");
sprintf(m_displayBuffer2, " ");
int j;
for (j = 1; j < argc; j++) {
strcat(m_displayBuffer1, m_displayBuffer2);
strcat(m_displayBuffer1, argv[j]);
}
LogDebug("%s", m_displayBuffer1);
}
}
/* Restart tokenizing */
argc = 0;
newtoken = 1;
break;
} /* switch( m_buffer[i] ) */
}
}
if (!m_screensDefined && m_connected)
defineScreens();
}
void CLCDproc::close()
{
}
int CLCDproc::socketPrintf(int fd, const char *format, ...)
{
char buf[BUFFER_MAX_LEN];
va_list ap;
va_start(ap, format);
int size = vsnprintf(buf, BUFFER_MAX_LEN, format, ap);
va_end(ap);
if (size < 0) {
LogError("LCDproc, socketPrintf: vsnprintf failed");
return -1;
}
if (size > BUFFER_MAX_LEN)
LogWarning("LCDproc, socketPrintf: vsnprintf truncated message");
FD_ZERO(&m_writefds); // empty writefds
FD_SET(m_socketfd, &m_writefds);
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
if (select(m_socketfd + 1, NULL, &m_writefds, NULL, &m_timeout) == -1)
LogError("LCDproc, error on select");
if (FD_ISSET(m_socketfd, &m_writefds)) {
if (send(m_socketfd, buf, int(strlen(buf) + 1U), 0) == -1) {
LogError("LCDproc, cannot send data");
return -1;
}
}
return 0;
}
void CLCDproc::defineScreens()
{
// The Status Screen
socketPrintf(m_socketfd, "screen_add Status");
socketPrintf(m_socketfd, "screen_set Status -name Status -heartbeat on -priority info -backlight %s", m_dimOnIdle ? "off" : "on");
socketPrintf(m_socketfd, "widget_add Status Callsign string");
socketPrintf(m_socketfd, "widget_add Status DMRNumber string");
socketPrintf(m_socketfd, "widget_add Status Title string");
socketPrintf(m_socketfd, "widget_add Status Status string");
socketPrintf(m_socketfd, "widget_add Status Time string");
socketPrintf(m_socketfd, "widget_add Status Date string");
socketPrintf(m_socketfd, "widget_set Status Callsign 1 1 %s", m_callsign.c_str());
socketPrintf(m_socketfd, "widget_set Status DMRNumber %u 1 %u", m_cols - 7, m_dmrid);
socketPrintf(m_socketfd, "widget_set Status Title 1 %u MMDVM", m_rows);
socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
// The DStar Screen
socketPrintf(m_socketfd, "screen_add DStar");
socketPrintf(m_socketfd, "screen_set DStar -name DStar -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DStar Mode string");
socketPrintf(m_socketfd, "widget_add DStar Line2 scroller");
socketPrintf(m_socketfd, "widget_add DStar Line3 scroller");
socketPrintf(m_socketfd, "widget_add DStar Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\"");
*/
// The DMR Screen
socketPrintf(m_socketfd, "screen_add DMR");
socketPrintf(m_socketfd, "screen_set DMR -name DMR -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DMR Mode string");
socketPrintf(m_socketfd, "widget_add DMR Slot1_ string");
socketPrintf(m_socketfd, "widget_add DMR Slot2_ string");
socketPrintf(m_socketfd, "widget_add DMR Slot1 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot2 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot1RSSI string");
socketPrintf(m_socketfd, "widget_add DMR Slot2RSSI string");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u 1", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u 2", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 2 15 2 h 3 Listening");
*/
// The YSF Screen
socketPrintf(m_socketfd, "screen_add YSF");
socketPrintf(m_socketfd, "screen_set YSF -name YSF -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add YSF Mode string");
socketPrintf(m_socketfd, "widget_add YSF Line2 scroller");
socketPrintf(m_socketfd, "widget_add YSF Line3 scroller");
socketPrintf(m_socketfd, "widget_add YSF Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set YSF Line2 2 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set YSF Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set YSF Line4 4 2 15 2 h 3 \" \"");
*/
// The P25 Screen
socketPrintf(m_socketfd, "screen_add P25");
socketPrintf(m_socketfd, "screen_set P25 -name P25 -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add P25 Mode string");
socketPrintf(m_socketfd, "widget_add P25 Line2 scroller");
socketPrintf(m_socketfd, "widget_add P25 Line3 scroller");
socketPrintf(m_socketfd, "widget_add P25 Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set P25 Line3 2 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set P25 Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set P25 Line4 4 2 15 2 h 3 \" \"");
*/
m_screensDefined = true;
}

82
LCDproc.h Normal file
View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2016, 2017 by Tony Corbett G0WFV
*
* 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(LCDproc_H)
#define LCDproc_H
#include "Display.h"
#include "Timer.h"
#include <string>
class CLCDproc : public CDisplay
{
public:
CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle);
virtual ~CLCDproc();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
std::string m_address;
unsigned int m_port;
unsigned int m_localPort;
std::string m_callsign;
unsigned int m_dmrid;
bool m_displayClock;
bool m_utc;
bool m_duplex;
bool m_dimOnIdle;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
int socketPrintf(int fd, const char *format, ...);
void defineScreens();
};
#endif

View file

@ -50,14 +50,20 @@ TXLevel=50
# YSFTXLevel=50
# P25TXLevel=50
OscOffset=0
RSSIMultiplier=1
RSSIOffset=10
RSSIMappingFile=RSSI.dat
SamplesDir=.
Debug=0
[UMP]
Enable=0
# Port=\\.\COM4
Port=/dev/ttyACM1
[D-Star]
Enable=1
Module=C
SelfOnly=0
ErrorReply=1
[DMR]
Enable=1
@ -65,26 +71,13 @@ Beacons=1
Id=123456
ColorCode=1
SelfOnly=0
EmbeddedLCOnly=0
DumpTAData=1
# Prefixes=234,235
# Slot1TGWhiteList=
# Slot2TGWhiteList=
CallHang=3
TXHang=4
#Blacklist=
#DstIdBlackListSlot1RF=
#DstIdBlackListSlot2RF=
#DstIdWhiteListSlot1RF=
#DstIdWhiteListSlot2RF=
#DstIdBlackListSlot1NET=
#DstIdBlackListSlot2NET=
#DstIdWhiteListSlot1NET=
#DstIdWhiteListSlot2NET=
TGRewriteSlot1=0
TGRewriteSlot2=0
BMAutoRewrite=0
BMRewriteReflectorVoicePrompts=0
DirectDial=0
TargetTG=9
#RewriteMapSlot1=
#RewritemapSlot2=
[System Fusion]
Enable=1
@ -108,7 +101,7 @@ Port=62031
Jitter=300
# Local=3350
Password=PASSWORD
RSSI=0
# Options=
Slot1=1
Slot2=1
Debug=0
@ -165,3 +158,11 @@ IdleBrightness=20
Type=3
Brightness=0
Invert=0
[LCDproc]
Address=localhost
Port=13666
#LocalPort=13667
DimOnIdle=0
DisplayClock=1
UTC=0

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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
@ -17,6 +17,7 @@
*/
#include "MMDVMHost.h"
#include "RSSIInterpolator.h"
#include "SerialController.h"
#include "ModemSerialPort.h"
#include "Version.h"
@ -29,6 +30,7 @@
#include "YSFControl.h"
#include "P25Control.h"
#include "Nextion.h"
#include "LCDproc.h"
#include "Thread.h"
#include "Log.h"
@ -126,6 +128,7 @@ m_dmrNetwork(NULL),
m_ysfNetwork(NULL),
m_p25Network(NULL),
m_display(NULL),
m_ump(NULL),
m_mode(MODE_IDLE),
m_rfModeHang(10U),
m_netModeHang(3U),
@ -189,7 +192,7 @@ int CMMDVMHost::run()
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
#if !defined(HD44780)
#if !defined(HD44780) && !defined(OLED)
//If we are currently root...
if (getuid() == 0) {
struct passwd* user = ::getpwnam("mmdvm");
@ -239,6 +242,20 @@ int CMMDVMHost::run()
if (!ret)
return 1;
if (m_conf.getUMPEnabled()) {
std::string port = m_conf.getUMPPort();
LogInfo("Universal MMDVM Peripheral");
LogInfo(" Port: %s", port.c_str());
m_ump = new CUMP(port);
bool ret = m_ump->open();
if (!ret) {
delete m_ump;
m_ump = NULL;
}
}
createDisplay();
if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) {
@ -280,6 +297,16 @@ int CMMDVMHost::run()
CTimer dmrBeaconTimer(1000U, 4U);
bool dmrBeaconsEnabled = m_dmrEnabled && m_conf.getDMRBeacons();
// For all modes we handle RSSI
std::string rssiMappingFile = m_conf.getModemRSSIMappingFile();
CRSSIInterpolator* rssi = new CRSSIInterpolator;
if (!rssiMappingFile.empty()) {
LogInfo("RSSI");
LogInfo(" Mapping File: %s", rssiMappingFile.c_str());
rssi->load(rssiMappingFile);
}
// For DMR and P25 we try to map IDs to callsigns
if (m_dmrEnabled || m_p25Enabled) {
std::string lookupFile = m_conf.getDMRIdLookupFile();
@ -302,41 +329,34 @@ int CMMDVMHost::run()
std::string module = m_conf.getDStarModule();
bool selfOnly = m_conf.getDStarSelfOnly();
std::vector<std::string> blackList = m_conf.getDStarBlackList();
bool errorReply = m_conf.getDStarErrorReply();
LogInfo("D-Star Parameters");
LogInfo(" Module: %s", module.c_str());
LogInfo(" Self Only: %s", selfOnly ? "yes" : "no");
LogInfo(" Error Reply: %s", errorReply ? "yes" : "no");
if (blackList.size() > 0U)
LogInfo(" Black List: %u", blackList.size());
dstar = new CDStarControl(m_callsign, module, selfOnly, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex);
dstar = new CDStarControl(m_callsign, module, selfOnly, errorReply, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex, rssi);
}
CDMRControl* dmr = NULL;
if (m_dmrEnabled) {
unsigned int id = m_conf.getDMRId();
unsigned int colorCode = m_conf.getDMRColorCode();
bool selfOnly = m_conf.getDMRSelfOnly();
bool TGRewriteSlot1 = m_conf.getDMRTGRewriteSlot1();
bool TGRewriteSlot2 = m_conf.getDMRTGRewriteSlot2();
bool BMAutoRewrite = m_conf.getDMRBMAutoRewrite();
bool BMRewriteReflectorVoicePrompts = m_conf.getDMRBMRewriteReflectorVoicePrompts();
std::vector<unsigned int> prefixes = m_conf.getDMRPrefixes();
unsigned int id = m_conf.getDMRId();
unsigned int colorCode = m_conf.getDMRColorCode();
bool selfOnly = m_conf.getDMRSelfOnly();
bool embeddedLCOnly = m_conf.getDMREmbeddedLCOnly();
bool dumpTAData = m_conf.getDMRDumpTAData();
std::vector<unsigned int> prefixes = m_conf.getDMRPrefixes();
std::vector<unsigned int> blackList = m_conf.getDMRBlackList();
std::vector<unsigned int> dstIDBlackListSlot1RF = m_conf.getDMRDstIdBlacklistSlot1RF();
std::vector<unsigned int> dstIDBlackListSlot2RF = m_conf.getDMRDstIdBlacklistSlot2RF();
std::vector<unsigned int> dstIDWhiteListSlot1RF = m_conf.getDMRDstIdWhitelistSlot1RF();
std::vector<unsigned int> dstIDWhiteListSlot2RF = m_conf.getDMRDstIdWhitelistSlot2RF();
std::vector<unsigned int> dstIDBlackListSlot1NET = m_conf.getDMRDstIdBlacklistSlot1NET();
std::vector<unsigned int> dstIDBlackListSlot2NET = m_conf.getDMRDstIdBlacklistSlot2NET();
std::vector<unsigned int> dstIDWhiteListSlot1NET = m_conf.getDMRDstIdWhitelistSlot1NET();
std::vector<unsigned int> dstIDWhiteListSlot2NET = m_conf.getDMRDstIdWhitelistSlot2NET();
unsigned int callHang = m_conf.getDMRCallHang();
unsigned int txHang = m_conf.getDMRTXHang();
int rssiMultiplier = m_conf.getModemRSSIMultiplier();
int rssiOffset = m_conf.getModemRSSIOffset();
unsigned int jitter = m_conf.getDMRNetworkJitter();
std::vector<unsigned int> whiteList = m_conf.getDMRWhiteList();
std::vector<unsigned int> slot1TGWhiteList = m_conf.getDMRSlot1TGWhiteList();
std::vector<unsigned int> slot2TGWhiteList = m_conf.getDMRSlot2TGWhiteList();
unsigned int callHang = m_conf.getDMRCallHang();
unsigned int txHang = m_conf.getDMRTXHang();
unsigned int jitter = m_conf.getDMRNetworkJitter();
if (txHang > m_rfModeHang)
txHang = m_rfModeHang;
@ -350,46 +370,23 @@ int CMMDVMHost::run()
LogInfo(" Id: %u", id);
LogInfo(" Color Code: %u", colorCode);
LogInfo(" Self Only: %s", selfOnly ? "yes" : "no");
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 (dstIDBlackListSlot1RF.size() > 0U)
LogInfo(" Slot 1 RF Destination ID Black List: %u entries", dstIDBlackListSlot1RF.size());
if (dstIDBlackListSlot2RF.size() > 0U)
LogInfo(" Slot 2 RF Destination ID Black List: %u entries", dstIDBlackListSlot2RF.size());
if (dstIDWhiteListSlot1RF.size() > 0U)
LogInfo(" Slot 1 RF Destination ID White List: %u entries", dstIDWhiteListSlot1RF.size());
if (dstIDWhiteListSlot2RF.size() > 0U)
LogInfo(" Slot 2 RF Destination ID White List: %u entries", dstIDWhiteListSlot2RF.size());
if (dstIDBlackListSlot1NET.size() > 0U)
LogInfo(" Slot 1 NET Destination ID Black List: %u entries", dstIDBlackListSlot1NET.size());
if (dstIDBlackListSlot2NET.size() > 0U)
LogInfo(" Slot 2 NET Destination ID Black List: %u entries", dstIDBlackListSlot2NET.size());
if (dstIDWhiteListSlot1NET.size() > 0U)
LogInfo(" Slot 1 NET Destination ID White List: %u entries", dstIDWhiteListSlot1NET.size());
if (dstIDWhiteListSlot2NET.size() > 0U)
LogInfo(" Slot 2 NET Destination ID White List: %u entries", dstIDWhiteListSlot2NET.size());
if (whiteList.size() > 0U)
LogInfo(" Source ID White List: %u", whiteList.size());
if (slot1TGWhiteList.size() > 0U)
LogInfo(" Slot 1 TG White List: %u", slot1TGWhiteList.size());
if (slot2TGWhiteList.size() > 0U)
LogInfo(" Slot 2 TG White List: %u", slot2TGWhiteList.size());
LogInfo(" Call Hang: %us", callHang);
LogInfo(" TX Hang: %us", txHang);
if (rssiMultiplier != 0) {
LogInfo(" RSSI Multiplier: %d", rssiMultiplier);
LogInfo(" RSSI Offset: %d", rssiOffset);
}
if (TGRewriteSlot1)
LogInfo(" TG Rewrite Slot 1 enabled");
if (TGRewriteSlot2)
LogInfo(" TG Rewrite Slot 2 enabled");
if (BMAutoRewrite)
LogInfo(" BrandMeister Auto Rewrite enabled");
if (BMRewriteReflectorVoicePrompts)
LogInfo(" BrandMeister Rewrite Reflector Voice Prompts enabled");
dmr = new CDMRControl(id, colorCode, callHang, selfOnly, prefixes, blackList,dstIDBlackListSlot1RF,dstIDWhiteListSlot1RF, dstIDBlackListSlot2RF, dstIDWhiteListSlot2RF, dstIDBlackListSlot1NET,dstIDWhiteListSlot1NET, dstIDBlackListSlot2NET, dstIDWhiteListSlot2NET, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssiMultiplier, rssiOffset, jitter, TGRewriteSlot1, TGRewriteSlot2, BMAutoRewrite, BMRewriteReflectorVoicePrompts);
dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi, jitter);
m_dmrTXTimer.setTimeout(txHang);
}
@ -401,7 +398,7 @@ int CMMDVMHost::run()
LogInfo("YSF Parameters");
LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no");
ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, m_timeout, m_duplex, remoteGateway);
ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, m_timeout, m_duplex, remoteGateway, rssi);
}
CP25Control* p25 = NULL;
@ -411,7 +408,7 @@ int CMMDVMHost::run()
LogInfo("P25 Parameters");
LogInfo(" NAC: $%03X", nac);
p25 = new CP25Control(nac, m_p25Network, m_display, m_timeout, m_duplex, m_lookup);
p25 = new CP25Control(nac, m_p25Network, m_display, m_timeout, m_duplex, m_lookup, rssi);
}
setMode(MODE_IDLE);
@ -419,10 +416,13 @@ int CMMDVMHost::run()
LogMessage("MMDVMHost-%s is running", VERSION);
while (!m_killed) {
bool lockout = m_modem->hasLockout();
if (lockout && m_mode != MODE_LOCKOUT)
bool lockout1 = m_modem->hasLockout();
bool lockout2 = false;
if (m_ump != NULL)
lockout2 = m_ump->getLockout();
if ((lockout1 || lockout2) && m_mode != MODE_LOCKOUT)
setMode(MODE_LOCKOUT);
else if (!lockout && m_mode == MODE_LOCKOUT)
else if ((!lockout1 && !lockout2) && m_mode == MODE_LOCKOUT)
setMode(MODE_IDLE);
bool error = m_modem->hasError();
@ -431,6 +431,13 @@ int CMMDVMHost::run()
else if (!error && m_mode == MODE_ERROR)
setMode(MODE_IDLE);
if (m_ump != NULL) {
bool tx = m_modem->hasTX();
m_ump->setTX(tx);
bool cd = m_modem->hasCD();
m_ump->setCD(cd);
}
unsigned char data[200U];
unsigned int len;
bool ret;
@ -718,6 +725,9 @@ int CMMDVMHost::run()
m_dmrTXTimer.stop();
}
if (m_ump != NULL)
m_ump->clock(ms);
if (ms < 5U)
CThread::sleep(5U);
}
@ -732,6 +742,11 @@ int CMMDVMHost::run()
m_display->close();
delete m_display;
if (m_ump != NULL) {
m_ump->close();
delete m_ump;
}
if (m_lookup != NULL)
m_lookup->stop();
@ -782,6 +797,7 @@ bool CMMDVMHost::createModem()
unsigned int rxFrequency = m_conf.getRxFrequency();
unsigned int txFrequency = m_conf.getTxFrequency();
int oscOffset = m_conf.getModemOscOffset();
std::string samplesDir = m_conf.getModemSamplesDir();
LogInfo("Modem Parameters");
LogInfo(" Port: %s", port.c_str());
@ -798,10 +814,9 @@ bool CMMDVMHost::createModem()
LogInfo(" P25 TX Level: %u%%", p25TXLevel);
LogInfo(" RX Frequency: %uHz", rxFrequency);
LogInfo(" TX Frequency: %uHz", txFrequency);
LogInfo(" Osc. Offset: %dppm", oscOffset);
m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, oscOffset, debug);
m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, oscOffset, samplesDir, debug);
m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled);
m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel);
m_modem->setRFParams(rxFrequency, txFrequency);
@ -854,7 +869,6 @@ bool CMMDVMHost::createDMRNetwork()
unsigned int jitter = m_conf.getDMRNetworkJitter();
bool slot1 = m_conf.getDMRNetworkSlot1();
bool slot2 = m_conf.getDMRNetworkSlot2();
bool rssi = m_conf.getDMRNetworkRSSI();
HW_TYPE hwType = m_modem->getHWType();
LogInfo("DMR Network Parameters");
@ -867,9 +881,14 @@ bool CMMDVMHost::createDMRNetwork()
LogInfo(" Jitter: %ums", jitter);
LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled");
LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled");
LogInfo(" RSSI: %s", rssi ? "enabled" : "disabled");
m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, rssi, hwType);
m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType);
std::string options = m_conf.getDMRNetworkOptions();
if (!options.empty()) {
LogInfo(" Options: %s", options.c_str());
m_dmrNetwork->setOptions(options);
}
unsigned int rxFrequency = m_conf.getRxFrequency();
unsigned int txFrequency = m_conf.getTxFrequency();
@ -1023,13 +1042,39 @@ void CMMDVMHost::createDisplay()
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
LogInfo(" Idle Brightness: %u", idleBrightness);
ISerialPort* serial = NULL;
if (port == "modem")
serial = new CModemSerialPort(m_modem);
else
serial = new CSerialController(port, SERIAL_9600);
if (port == "modem") {
ISerialPort* serial = new CModemSerialPort(m_modem);
m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness);
} else if (port == "ump") {
if (m_ump != NULL)
m_display = new CNextion(m_callsign, dmrid, m_ump, brightness, displayClock, utc, idleBrightness);
} else {
ISerialPort* serial = new CSerialController(port, SERIAL_9600);
m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness);
}
} 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();
m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness);
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();
@ -1070,15 +1115,21 @@ void CMMDVMHost::createDisplay()
#if defined(OLED)
} else if (type == "OLED") {
unsigned char displayType = m_conf.getOLEDType();
unsigned char displayBrightness = m_conf.getOLEDBrightness();
unsigned char displayInvert = m_conf.getOLEDInvert();
m_display = new COLED(displayType, displayBrightness, displayInvert);
unsigned char type = m_conf.getOLEDType();
unsigned char brightness = m_conf.getOLEDBrightness();
bool invert = m_conf.getOLEDInvert();
m_display = new COLED(type, brightness, invert);
#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;
@ -1100,6 +1151,8 @@ void CMMDVMHost::setMode(unsigned char mode)
if (m_p25Network != NULL)
m_p25Network->enable(false);
m_modem->setMode(MODE_DSTAR);
if (m_ump != NULL)
m_ump->setMode(MODE_DSTAR);
m_mode = MODE_DSTAR;
m_modeTimer.start();
m_cwIdTimer.stop();
@ -1113,6 +1166,8 @@ void CMMDVMHost::setMode(unsigned char mode)
if (m_p25Network != NULL)
m_p25Network->enable(false);
m_modem->setMode(MODE_DMR);
if (m_ump != NULL)
m_ump->setMode(MODE_DMR);
if (m_duplex) {
m_modem->writeDMRStart(true);
m_dmrTXTimer.start();
@ -1130,6 +1185,8 @@ void CMMDVMHost::setMode(unsigned char mode)
if (m_p25Network != NULL)
m_p25Network->enable(false);
m_modem->setMode(MODE_YSF);
if (m_ump != NULL)
m_ump->setMode(MODE_YSF);
m_mode = MODE_YSF;
m_modeTimer.start();
m_cwIdTimer.stop();
@ -1143,6 +1200,8 @@ void CMMDVMHost::setMode(unsigned char mode)
if (m_ysfNetwork != NULL)
m_ysfNetwork->enable(false);
m_modem->setMode(MODE_P25);
if (m_ump != NULL)
m_ump->setMode(MODE_P25);
m_mode = MODE_P25;
m_modeTimer.start();
m_cwIdTimer.stop();
@ -1163,6 +1222,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_dmrTXTimer.stop();
}
m_modem->setMode(MODE_IDLE);
if (m_ump != NULL)
m_ump->setMode(MODE_IDLE);
m_display->setLockout();
m_mode = MODE_LOCKOUT;
m_modeTimer.stop();
@ -1183,6 +1244,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_modem->writeDMRStart(false);
m_dmrTXTimer.stop();
}
if (m_ump != NULL)
m_ump->setMode(MODE_IDLE);
m_display->setError("MODEM");
m_mode = MODE_ERROR;
m_modeTimer.stop();
@ -1203,6 +1266,8 @@ void CMMDVMHost::setMode(unsigned char mode)
m_dmrTXTimer.stop();
}
m_modem->setMode(MODE_IDLE);
if (m_ump != NULL)
m_ump->setMode(MODE_IDLE);
if (m_mode == MODE_ERROR || m_mode == MODE_LOCKOUT) {
m_modem->sendCWId(m_callsign);
m_cwIdTimer.setTimeout(m_cwIdTime);

View file

@ -28,6 +28,7 @@
#include "Timer.h"
#include "Modem.h"
#include "Conf.h"
#include "UMP.h"
#include <string>
@ -47,6 +48,7 @@ private:
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
CDisplay* m_display;
CUMP* m_ump;
unsigned char m_mode;
unsigned int m_rfModeHang;
unsigned int m_netModeHang;

View file

@ -160,7 +160,7 @@
<ClInclude Include="DMRDataHeader.h" />
<ClInclude Include="DMRDefines.h" />
<ClInclude Include="DMREMB.h" />
<ClInclude Include="DMREmbeddedLC.h" />
<ClInclude Include="DMREmbeddedData.h" />
<ClInclude Include="DMRFullLC.h" />
<ClInclude Include="DMRLC.h" />
<ClInclude Include="DMRNetwork.h" />
@ -177,6 +177,7 @@
<ClInclude Include="Golay24128.h" />
<ClInclude Include="Hamming.h" />
<ClInclude Include="DMRLookup.h" />
<ClInclude Include="LCDproc.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="MMDVMHost.h" />
<ClInclude Include="Modem.h" />
@ -196,6 +197,7 @@
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="RS129.h" />
<ClInclude Include="RS241213.h" />
<ClInclude Include="RSSIInterpolator.h" />
<ClInclude Include="SerialController.h" />
<ClInclude Include="SerialPort.h" />
<ClInclude Include="SHA256.h" />
@ -205,6 +207,7 @@
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPSocket.h" />
<ClInclude Include="UMP.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="YSFControl.h" />
@ -227,7 +230,7 @@
<ClCompile Include="DMRData.cpp" />
<ClCompile Include="DMRDataHeader.cpp" />
<ClCompile Include="DMREMB.cpp" />
<ClCompile Include="DMREmbeddedLC.cpp" />
<ClCompile Include="DMREmbeddedData.cpp" />
<ClCompile Include="DMRFullLC.cpp" />
<ClCompile Include="DMRLC.cpp" />
<ClCompile Include="DMRLookup.cpp" />
@ -243,6 +246,7 @@
<ClCompile Include="Golay2087.cpp" />
<ClCompile Include="Golay24128.cpp" />
<ClCompile Include="Hamming.cpp" />
<ClCompile Include="LCDproc.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="MMDVMHost.cpp" />
<ClCompile Include="Modem.cpp" />
@ -260,6 +264,7 @@
<ClCompile Include="QR1676.cpp" />
<ClCompile Include="RS129.cpp" />
<ClCompile Include="RS241213.cpp" />
<ClCompile Include="RSSIInterpolator.cpp" />
<ClCompile Include="SerialController.cpp" />
<ClCompile Include="SerialPort.cpp" />
<ClCompile Include="SHA256.cpp" />
@ -269,6 +274,7 @@
<ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPSocket.cpp" />
<ClCompile Include="UMP.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="YSFNetwork.cpp" />
<ClCompile Include="YSFPayload.cpp" />

View file

@ -131,7 +131,7 @@
<ClInclude Include="DMREMB.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMREmbeddedLC.h">
<ClInclude Include="DMREmbeddedData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRFullLC.h">
@ -212,6 +212,15 @@
<ClInclude Include="Mutex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LCDproc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UMP.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RSSIInterpolator.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BPTC19696.cpp">
@ -316,7 +325,7 @@
<ClCompile Include="DMREMB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMREmbeddedLC.cpp">
<ClCompile Include="DMREmbeddedData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRFullLC.cpp">
@ -394,5 +403,14 @@
<ClCompile Include="Mutex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LCDproc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UMP.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RSSIInterpolator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -7,11 +7,11 @@ LIBS = -lpthread
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost

26
Makefile.Pi Normal file
View file

@ -0,0 +1,26 @@
# This makefile is for use with the Raspberry Pi. The wiringpi library is needed.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DRASPBERRY_PI -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
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 Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost
MMDVMHost: $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~

View file

@ -7,11 +7,11 @@ LIBS = -lwiringPi -lwiringPiDev -lpthread
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o \
P25LowSpeedData.o P25Network.o P25NID.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
all: MMDVMHost

View file

@ -7,11 +7,11 @@ LIBS = -lwiringPi -lwiringPiDev -lpthread
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost

View file

@ -7,11 +7,11 @@ LIBS = -lArduiPi_OLED -lpthread
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost

View file

@ -7,11 +7,11 @@ LIBS = -lwiringPi -lwiringPiDev -lpthread
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost

View file

@ -7,11 +7,11 @@ LIBS = -lpthread -lsocket
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 DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \
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 Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \
Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \
P25Network.o P25NID.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
all: MMDVMHost

141
Modem.cpp
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2011-2017 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
@ -29,6 +29,7 @@
#include <cstdio>
#include <cassert>
#include <cstdint>
#include <ctime>
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
@ -71,6 +72,8 @@ const unsigned char MMDVM_NAK = 0x7FU;
const unsigned char MMDVM_SERIAL = 0x80U;
const unsigned char MMDVM_SAMPLES = 0xF0U;
const unsigned char MMDVM_DEBUG1 = 0xF1U;
const unsigned char MMDVM_DEBUG2 = 0xF2U;
const unsigned char MMDVM_DEBUG3 = 0xF3U;
@ -79,10 +82,10 @@ const unsigned char MMDVM_DEBUG5 = 0xF5U;
const unsigned int MAX_RESPONSES = 30U;
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int BUFFER_LENGTH = 2000U;
CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, bool debug) :
CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, const std::string& samplesDir, bool debug) :
m_port(port),
m_colorCode(0U),
m_duplex(duplex),
@ -98,6 +101,7 @@ m_dmrTXLevel(0U),
m_ysfTXLevel(0U),
m_p25TXLevel(0U),
m_oscOffset(oscOffset),
m_samplesDir(samplesDir),
m_debug(debug),
m_rxFrequency(0U),
m_txFrequency(0U),
@ -128,6 +132,7 @@ m_dmrSpace2(0U),
m_ysfSpace(0U),
m_p25Space(0U),
m_tx(false),
m_cd(false),
m_lockout(false),
m_error(false),
m_hwType(HWT_UNKNOWN)
@ -439,6 +444,8 @@ 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];
@ -447,7 +454,7 @@ void CModem::clock(unsigned int ms)
m_p25Space = m_buffer[10U];
m_inactivityTimer.start();
// LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u lockout=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, int(m_lockout));
// 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));
}
break;
@ -460,6 +467,18 @@ void CModem::clock(unsigned int ms)
LogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[3U], m_buffer[4U]);
break;
case MMDVM_DEBUG1:
case MMDVM_DEBUG2:
case MMDVM_DEBUG3:
case MMDVM_DEBUG4:
case MMDVM_DEBUG5:
printDebug();
break;
case MMDVM_SAMPLES:
dumpSamples();
break;
default:
LogMessage("Unknown message, type: %02X", m_buffer[2U]);
CUtils::dump("Buffer dump", m_buffer, m_length);
@ -852,6 +871,11 @@ bool CModem::hasTX() const
return m_tx;
}
bool CModem::hasCD() const
{
return m_cd;
}
bool CModem::hasLockout() const
{
return m_lockout;
@ -1102,42 +1126,26 @@ RESP_TYPE_MMDVM CModem::getResponse()
if (ret == 0)
return RTM_TIMEOUT;
switch (m_buffer[2U]) {
case MMDVM_DSTAR_HEADER:
case MMDVM_DSTAR_DATA:
case MMDVM_DSTAR_LOST:
case MMDVM_DSTAR_EOT:
case MMDVM_DMR_DATA1:
case MMDVM_DMR_DATA2:
case MMDVM_DMR_LOST1:
case MMDVM_DMR_LOST2:
case MMDVM_YSF_DATA:
case MMDVM_YSF_LOST:
case MMDVM_P25_HDR:
case MMDVM_P25_LDU:
case MMDVM_P25_LOST:
case MMDVM_GET_STATUS:
case MMDVM_GET_VERSION:
case MMDVM_ACK:
case MMDVM_NAK:
case MMDVM_SERIAL:
case MMDVM_DEBUG1:
case MMDVM_DEBUG2:
case MMDVM_DEBUG3:
case MMDVM_DEBUG4:
case MMDVM_DEBUG5:
break;
default:
LogError("Unknown message, type: %02X", m_buffer[2U]);
m_offset = 0U;
return RTM_ERROR;
}
m_offset = 3U;
}
if (m_offset >= 3U) {
// Use later two byte length field
if (m_length == 0U) {
int ret = m_serial.read(m_buffer + 3U, 2U);
if (ret < 0) {
LogError("Error when reading from the modem");
m_offset = 0U;
return RTM_ERROR;
}
if (ret == 0)
return RTM_TIMEOUT;
m_length = (m_buffer[3U] << 8) | m_buffer[4U];
m_offset = 5U;
}
while (m_offset < m_length) {
int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset);
if (ret < 0) {
@ -1156,19 +1164,9 @@ RESP_TYPE_MMDVM CModem::getResponse()
m_offset = 0U;
switch (m_buffer[2U]) {
case MMDVM_DEBUG1:
case MMDVM_DEBUG2:
case MMDVM_DEBUG3:
case MMDVM_DEBUG4:
case MMDVM_DEBUG5:
printDebug();
return RTM_TIMEOUT;
// CUtils::dump(1U, "Received", m_buffer, m_length);
default:
// CUtils::dump(1U, "Received", m_buffer, m_length);
return RTM_OK;
}
return RTM_OK;
}
HW_TYPE CModem::getHWType() const
@ -1296,3 +1294,50 @@ void CModem::printDebug()
LogMessage("Debug: %.*s %d %d %d %d", m_length - 11U, m_buffer + 3U, val1, val2, val3, val4);
}
}
void CModem::dumpSamples()
{
if (m_samplesDir.empty())
m_samplesDir = ".";
time_t now;
::time(&now);
struct tm* tm = ::localtime(&now);
const char* mode = NULL;
switch (m_buffer[5U]) {
case MODE_DSTAR:
mode = "DStar";
break;
case MODE_DMR:
mode = "DMR";
break;
case MODE_P25:
mode = "P25";
break;
case MODE_YSF:
mode = "YSF";
break;
default:
LogWarning("Unknown protocol passed to samples dump - %u", m_buffer[5U]);
return;
}
char filename[150U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\Samples-%s-%04d%02d%02d.dat", m_samplesDir.c_str(), mode, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#else
::sprintf(filename, "%s/Samples-%s-%04d%02d%02d.dat", m_samplesDir.c_str(), mode, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#endif
FILE* fp = ::fopen(filename, "a+b");
if (fp == NULL) {
LogWarning("Unable to open samples file for writing - %s", filename);
return;
}
::fwrite(m_buffer + 6U, 1U, m_length - 6U, fp);
::fclose(fp);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2011-2017 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
@ -34,7 +34,7 @@ enum RESP_TYPE_MMDVM {
class CModem {
public:
CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, bool debug = false);
CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, int oscOffset, const std::string& samplesDir, bool debug = false);
~CModem();
void setRFParams(unsigned int rxFrequency, unsigned int txFrequency);
@ -59,6 +59,7 @@ public:
bool hasP25Space() const;
bool hasTX() const;
bool hasCD() const;
bool hasLockout() const;
bool hasError() const;
@ -101,6 +102,7 @@ private:
unsigned int m_ysfTXLevel;
unsigned int m_p25TXLevel;
int m_oscOffset;
std::string m_samplesDir;
bool m_debug;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
@ -131,6 +133,7 @@ private:
unsigned int m_ysfSpace;
unsigned int m_p25Space;
bool m_tx;
bool m_cd;
bool m_lockout;
bool m_error;
HW_TYPE m_hwType;
@ -141,6 +144,7 @@ private:
bool setFrequency();
void printDebug();
void dumpSamples();
RESP_TYPE_MMDVM getResponse();
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -25,6 +25,15 @@
#include <ctime>
#include <clocale>
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DSTAR_BER_COUNT = 63U; // 63 * 20ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int DMR_BER_COUNT = 24U; // 24 * 60ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int YSF_BER_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
const unsigned int P25_BER_COUNT = 7U; // 7 * 180ms = 1260ms
CNextion::CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness) :
CDisplay(),
m_callsign(callsign),
@ -35,7 +44,15 @@ m_mode(MODE_IDLE),
m_displayClock(displayClock),
m_utc(utc),
m_idleBrightness(idleBrightness),
m_clockDisplayTimer(1000U, 0U, 400U)
m_clockDisplayTimer(1000U, 0U, 400U),
m_rssiAccum1(0U),
m_rssiAccum2(0U),
m_berAccum1(0.0F),
m_berAccum2(0.0F),
m_rssiCount1(0U),
m_rssiCount2(0U),
m_berCount1(0U),
m_berCount2(0U)
{
assert(serial != NULL);
assert(brightness >= 0U && brightness <= 100U);
@ -143,6 +160,54 @@ void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your,
m_clockDisplayTimer.stop();
m_mode = MODE_DSTAR;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == DSTAR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / DSTAR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeDStarBERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == DSTAR_BER_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(DSTAR_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearDStarInt()
@ -150,6 +215,8 @@ void CNextion::clearDStarInt()
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
sendCommand("t4.txt=\"\"");
}
void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
@ -186,6 +253,100 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
m_clockDisplayTimer.stop();
m_mode = MODE_DMR;
m_rssiAccum1 = 0U;
m_rssiAccum2 = 0U;
m_berAccum1 = 0.0F;
m_berAccum2 = 0.0F;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
m_berCount1 = 0U;
m_berCount2 = 0U;
}
void CNextion::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (slotNo == 1U) {
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == DMR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"-%udBm\"", m_rssiAccum1 / DMR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
} else {
if (m_rssiCount2 == 0U) {
char text[20U];
::sprintf(text, "t5.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount2 = 1U;
return;
}
m_rssiAccum2 += rssi;
m_rssiCount2++;
if (m_rssiCount2 == DMR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t5.txt=\"-%udBm\"", m_rssiAccum2 / DMR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum2 = 0U;
m_rssiCount2 = 1U;
}
}
}
void CNextion::writeDMRBERInt(unsigned int slotNo, float ber)
{
if (slotNo == 1U) {
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t6.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == DMR_BER_COUNT) {
char text[20U];
::sprintf(text, "t6.txt=\"%.1f%%\"", m_berAccum1 / DMR_BER_COUNT);
sendCommand(text);
m_berAccum1 = 0U;
m_berCount1 = 1U;
}
} else {
if (m_berCount2 == 0U) {
char text[20U];
::sprintf(text, "t7.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount2 = 1U;
return;
}
m_berAccum2 += ber;
m_berCount2++;
if (m_berCount2 == DMR_BER_COUNT) {
char text[20U];
::sprintf(text, "t7.txt=\"%.1f%%\"", m_berAccum2 / DMR_BER_COUNT);
sendCommand(text);
m_berAccum2 = 0U;
m_berCount2 = 1U;
}
}
}
void CNextion::clearDMRInt(unsigned int slotNo)
@ -193,9 +354,13 @@ void CNextion::clearDMRInt(unsigned int slotNo)
if (slotNo == 1U) {
sendCommand("t0.txt=\"1 Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t4.txt=\"\"");
sendCommand("t6.txt=\"\"");
} else {
sendCommand("t2.txt=\"2 Listening\"");
sendCommand("t3.txt=\"\"");
sendCommand("t5.txt=\"\"");
sendCommand("t7.txt=\"\"");
}
}
@ -226,6 +391,54 @@ void CNextion::writeFusionInt(const char* source, const char* dest, const char*
m_clockDisplayTimer.stop();
m_mode = MODE_YSF;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == YSF_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / YSF_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeFusionBERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == YSF_BER_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(YSF_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearFusionInt()
@ -233,6 +446,8 @@ void CNextion::clearFusionInt()
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
sendCommand("t4.txt=\"\"");
}
void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
@ -256,6 +471,54 @@ void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, co
m_clockDisplayTimer.stop();
m_mode = MODE_P25;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t2.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == P25_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t2.txt=\"-%udBm\"", m_rssiAccum1 / P25_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeP25BERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == P25_BER_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"%.1f%%\"", m_berAccum1 / float(P25_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearP25Int()
@ -263,6 +526,7 @@ void CNextion::clearP25Int()
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
}
void CNextion::writeCWInt()

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -42,15 +42,23 @@ protected:
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void writeDStarBERInt(float ber);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void writeDMRBERInt(unsigned int slotNo, float ber);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void writeFusionBERInt(float ber);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void writeP25BERInt(float ber);
virtual void clearP25Int();
virtual void writeCWInt();
@ -68,6 +76,14 @@ private:
bool m_utc;
unsigned int m_idleBrightness;
CTimer m_clockDisplayTimer;
unsigned int m_rssiAccum1;
unsigned int m_rssiAccum2;
float m_berAccum1;
float m_berAccum2;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
unsigned int m_berCount1;
unsigned int m_berCount2;
void sendCommand(const char* command);
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Nextion/NX3224K024.HMI Normal file

Binary file not shown.

BIN
Nextion/NX3224K024.tft Normal file

Binary file not shown.

BIN
Nextion/NX3224K028.HMI Normal file

Binary file not shown.

BIN
Nextion/NX3224K028.tft Normal file

Binary file not shown.

BIN
Nextion/NX3224T024.HMI Normal file

Binary file not shown.

BIN
Nextion/NX3224T028.HMI Normal file

Binary file not shown.

BIN
Nextion/NX4024K032.HMI Normal file

Binary file not shown.

BIN
Nextion/NX4024T032.HMI Normal file

Binary file not shown.

BIN
Nextion/NX4024T032.tft Normal file

Binary file not shown.

BIN
Nextion/NX4827K043.HMI Normal file

Binary file not shown.

BIN
Nextion/NX4827K043.tft Normal file

Binary file not shown.

BIN
Nextion/NX4827T043.HMI Normal file

Binary file not shown.

BIN
Nextion/NX4827T043.tft Normal file

Binary file not shown.

BIN
Nextion/NX4832K035.tft Normal file

Binary file not shown.

BIN
Nextion/NX4832T035.HMI Normal file

Binary file not shown.

66
Nextion/README.md Normal file
View file

@ -0,0 +1,66 @@
# Update Nextion Displays from the Command Line
This directory contains a simple python script which you can use to update the
Nextion displays. All you need is a compiled .tft file which is written to the
display's flash memory. The precompiled .tft files with the MMDVMHost default
layout are to be found in this directory as well.
To update the Nextion display you just need to know the serial port the display
is connected to. It could be /dev/ttyUSBx for USB<->Serial adapters or
/dev/ttyAMA0 for the UART on the Raspberry Pi for example.
# Prerequisites
You need to have python installed as well as the python-serial package. That can
normally be found in your distro's package manager.
# File Naming Convention
There are compiled .tft files in the repo for basic and enhanced Nextion
displays of sizes 2.4", 2.8", 3.2" and 3.5". Please choose depending on the
model number printed on the back of the display.
The basic displays are denoted by a "T" as 7th character in the filename and
model number whereas enhanced displays have a "K" in that position. The actual
display size can be derived from the last two digits. The four digits in between
the characters refert to the diplay's resolution.
For example: A model number NX4832T035 represents a display with:
- a resolution of 320x480 pixels
- basic command set
- a screen size of 3.5"
# Updating the Display
Now comes the easy part. Just execute:
```
$ python nextion.py NX4832T035.tft /dev/ttyUSB0
```
And the output should be as follows:
```
Trying with baudrate: 2400...
Trying with baudrate: 4800...
Trying with baudrate: 9600...
Connected with baudrate: 9600...
Status: comok
Touchscreen: yes
Model: NX4832T035_011R
Firmware version: 68
MCU code: 61488
Serial: D2658880C35D2124
Flash size: 16777216
Downloading, 100%...
File transferred successfully
```
# Known errors
Especially when using USB<->Serial adapters there are cases in which the scripts
stops at different times. This is known and due to the very simple update
protocol. In this case you have to fix the display by using a SD-Card to update
the display. The /dev/ttyAMAx ports do not seems to suffer from this issue.

View file

@ -1,120 +1,136 @@
#!/usr/bin/python
# coding=utf-8
'''
* Copyright (C) 2016 Alex Koren
*
* 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.
'''
import threading
import time
import os
import sys
import serial
import time
import sys
import os
import re
e = "\xff\xff\xff"
PORT = '/dev/ttyAMA0'
BAUDCOMM = 9600
BAUDUPLOAD = 115200
CHECK_MODEL = 'NX3224T024'
def getBaudrate(ser, fSize=None, checkModel=None):
for baudrate in (2400, 4800, 9600, 19200, 38400, 57600, 115200):
ser.baudrate = baudrate
ser.timeout = 3000 / baudrate + .2
print 'Trying with baudrate: ' + str(baudrate) + '...'
ser.write("\xff\xff\xff")
ser.write('connect')
ser.write("\xff\xff\xff")
r = ser.read(128)
if 'comok' in r:
print 'Connected with baudrate: ' + str(baudrate) + '...'
noConnect = False
status, unknown1, model, fwversion, mcucode, serial, flashSize = r.strip("\xff\x00").split(',')
print 'Status: ' + status.split(' ')[0]
if status.split(' ')[1] == "1":
print 'Touchscreen: yes'
else:
print 'Touchscreen: no'
print 'Model: ' + model
print 'Firmware version: ' + fwversion
print 'MCU code: ' + mcucode
print 'Serial: ' + serial
print 'Flash size: ' + flashSize
if fSize and fSize > flashSize:
print 'File too big!'
return False
if checkModel and not checkModel in model:
print 'Wrong Display!'
return False
return True
return False
if len(sys.argv) != 2:
print 'usage: python %s file_to_upload.tft' % sys.argv[0]
exit(-2)
def setDownloadBaudrate(ser, fSize, baudrate):
ser.write("")
ser.write("whmi-wri " + str(fSize) + "," + str(baudrate) + ",0" + e)
time.sleep(.05)
ser.baudrate = baudrate
ser.timeout = .5
r = ser.read(1)
if "\x05" in r:
return True
return False
file_path = sys.argv[1]
if os.path.isfile(file_path):
print 'uploading %s (%i bytes)...' % (file_path, os.path.getsize(file_path))
else:
print 'file not found'
exit(-1)
fsize = os.path.getsize(file_path)
print('Filesize: ' + str(fsize))
ser = serial.Serial(PORT, BAUDCOMM, timeout=.1, )
acked = threading.Event()
stop_thread = threading.Event()
def reader():
global acked
global ser
while stop_thread.is_set() == False:
r = ser.read(1)
if r == '':
continue
elif '\x05' in r:
acked.set()
continue
else:
print '<%r>' % r
continue
def upload():
global acked
global ser
global stop_thread
ser.write('tjchmi-wri %i,%i,0' % (fsize, BAUDUPLOAD))
ser.write("\xff\xff\xff")
ser.flush()
acked.clear()
ser.baudrate = BAUDUPLOAD
ser.timeout = 0.1
threader.start()
print 'Waiting for ACK...'
acked.wait()
print 'Uploading...'
with open(file_path, 'rb') as hmif:
def transferFile(ser, filename, fSize):
with open(filename, 'rb') as hmif:
dcount = 0
while True:
#time.sleep(.1)
data = hmif.read(4096)
if len(data) == 0: break
if len(data) == 0:
break
dcount += len(data)
#print 'writing %i...' % len(data)
ser.timeout = 5
ser.write(data)
acked.clear()
sys.stdout.write('\rDownloading, %3.1f%%...' % (dcount/ float(fsize)*100.0))
sys.stdout.write('\rDownloading, %3.1f%%...'% (dcount / float(fSize) * 100.0))
sys.stdout.flush()
#print 'waiting for hmi...'
acked.wait()
print('')
stop_thread.set()
threader.join(1)
ser.timeout = .5
time.sleep(.5)
r = ser.read(1)
if "\x05" in r:
continue
else:
print
return False
break
print
return True
def upload(ser, filename, checkModel=None):
if not getBaudrate(ser, os.path.getsize(filename), checkModel):
print 'Could not find baudrate'
exit(1)
threader = threading.Thread(target = reader)
threader.daemon = True
if not setDownloadBaudrate(ser, os.path.getsize(filename), 115200):
print 'Could not set download baudrate'
exit(1)
no_connect = True
for baudrate in (2400, 4800, 9600, 19200, 38400, 57600, 115200):
ser.baudrate = baudrate
ser.timeout = 3000/baudrate + 0.2
print('Trying with ' + str(baudrate) + '...')
ser.write("\xff\xff\xff")
ser.write('connect')
ser.write("\xff\xff\xff")
r = ser.read(128)
if 'comok' in r:
print('Connected with ' + str(baudrate) + '!')
no_connect = False
status, unknown1, model, unknown2, version, serial, flash_size = r.strip("\xff\x00").split(',')
print('Status: ' + status)
print('Model: ' + model)
print('Version: ' + version)
print('Serial: ' + serial)
print('Flash size: ' + flash_size)
if fsize > flash_size:
print('File too big!')
break
if not CHECK_MODEL in model:
print('Wrong Display!')
break
upload()
break
if not transferFile(ser, filename, os.path.getsize(filename)):
print 'Could not transfer file'
exit(1)
if no_connect:
print('No connection!')
else:
print('File written to Display!')
print 'File transferred successfully'
exit(0)
ser.close()
if __name__ == "__main__":
if len(sys.argv) != 4 and len(sys.argv) != 3:
print 'usage:\npython nextion.py file_to_upload.tft /path/to/dev/ttyDevice [nextion_model_name]\
\nexample: nextion.py newUI.tft /dev/ttyUSB0 NX3224T024\
\nnote: model name is optional'
exit(1)
try:
ser = serial.Serial(sys.argv[2], 9600, timeout=5)
except serial.serialutil.SerialException:
print 'could not open serial device ' + sys.argv[2]
exit(1)
if serial.VERSION <= "3.0":
if not ser.isOpen():
ser.open()
else:
if not ser.is_open:
ser.open()
checkModel = None
if len(sys.argv) == 4:
checkModel = sys.argv[3]
pattern = re.compile("^NX\d{4}[TK]\d{3}$")
if not pattern.match(checkModel):
print 'Invalid model name. Please give a correct one (e.g. NX3224T024)'
exit(1)
upload(ser, sys.argv[1], checkModel)

View file

@ -18,6 +18,12 @@
#include "NullDisplay.h"
#if defined(RASPBERRY_PI)
#include <wiringPi.h>
#endif
#define LED_STATUS 28
CNullDisplay::CNullDisplay() :
CDisplay()
{
@ -29,6 +35,12 @@ CNullDisplay::~CNullDisplay()
bool CNullDisplay::open()
{
#if defined(RASPBERRY_PI)
::wiringPiSetup();
::pinMode(LED_STATUS, OUTPUT);
::digitalWrite(LED_STATUS, 0);
#endif
return true;
}
@ -46,34 +58,58 @@ void CNullDisplay::setLockoutInt()
void CNullDisplay::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearDStarInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearDMRInt(unsigned int slotNo)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearFusionInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearP25Int()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeCWInt()

View file

@ -18,7 +18,65 @@
#include "OLED.h"
COLED::COLED(unsigned char displayType, unsigned char displayBrightness, unsigned char displayInvert) :
static unsigned char logo_glcd_bmp[] =
{ 0b00101011, 0b11010100,
0b01010111, 0b11101010,
0b01010111, 0b11101010,
0b00101011, 0b11010100,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000000, 0b00000000 };
//DMR 48x16 px
static unsigned char logo_dmr_bmp[] =
{ 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b10111111, 0b11111000, 0b01111000, 0b00011110, 0b01111111, 0b11100001,
0b10111111, 0b11111110, 0b01111100, 0b00111110, 0b01100000, 0b00011001,
0b10110000, 0b00001110, 0b01100110, 0b01100110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100011, 0b11000110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100001, 0b10000110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01111111, 0b11111001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01111000, 0b00000001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01101100, 0b00000001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01100110, 0b00000001,
0b10110000, 0b00001110, 0b01100000, 0b00000110, 0b01100011, 0b00000001,
0b10111111, 0b11111110, 0b01100000, 0b00000110, 0b01100001, 0b10000001,
0b10011111, 0b11111000, 0b01100000, 0b00000110, 0b01100000, 0b11000001,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111
};
//D-Star 64x16 px
static unsigned char logo_dstar_bmp[] =
{ 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b10000001, 0b11111100, 0b00000000, 0b00111100, 0b00000000, 0b00000000, 0b00000000, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b01000010, 0b01000000, 0b00000000, 0b00000000, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b01000000, 0b01000000, 0b00000000, 0b00000000, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b01000000, 0b01000000, 0b00000000, 0b00000000, 0b01000001,
0b10000001, 0b00000010, 0b01111111, 0b00111100, 0b01111000, 0b00111100, 0b00111100, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b00000010, 0b01000000, 0b00000010, 0b01000010, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b00000010, 0b01000000, 0b00111110, 0b01000000, 0b01000001,
0b10000001, 0b00000010, 0b00000000, 0b00000010, 0b01000000, 0b01000010, 0b01000000, 0b00000001,
0b10000001, 0b00000010, 0b00000000, 0b01000010, 0b01000010, 0b01000010, 0b01000000, 0b01000001,
0b10000001, 0b11111100, 0b00000000, 0b00111100, 0b00111100, 0b00111100, 0b01000000, 0b01000001,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111
};
COLED::COLED(unsigned char displayType, unsigned char displayBrightness, bool displayInvert) :
m_displayType(displayType),
m_displayBrightness(displayBrightness),
m_displayInvert(displayInvert)
@ -47,8 +105,8 @@ bool COLED::open()
display.begin();
display.invertDisplay(m_displayInvert);
if (m_displayBrightness > 0)
display.invertDisplay(m_displayInvert ? 1 : 0);
if (m_displayBrightness > 0U)
display.setBrightness(m_displayBrightness);
// init done
@ -70,7 +128,7 @@ void COLED::setIdleInt()
display.clearDisplay();
OLED_statusbar();
display.setCursor(0,display.height()/2);
display.setTextSize(3);
display.setTextSize(2);
display.print("Idle");
display.setTextSize(1);
display.display();
@ -233,6 +291,28 @@ void COLED::clearP25Int()
display.display();
}
void COLED::writeCWInt()
{
display.clearDisplay();
display.setCursor(0,display.height()/2);
display.setTextSize(3);
display.print("CW TX");
display.setTextSize(1);
display.display();
display.startscrollright(0x02,0x0f);
}
void COLED::clearCWInt()
{
display.clearDisplay();
display.setCursor(0,display.height()/2);
display.setTextSize(2);
display.print("Idle");
display.setTextSize(1);
display.display();
display.startscrollright(0x02,0x0f);
}
void COLED::close()
{
display.close();
@ -243,11 +323,11 @@ void COLED::OLED_statusbar()
display.stopscroll();
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.setCursor(0,0);
if (m_mode == MODE_DMR)
display.drawBitmap(0, 0, logo_dmr_bmp, 48, 16, WHITE);
else if (m_mode == MODE_DSTAR)
display.print("D-Star");
display.drawBitmap(0, 0, logo_dstar_bmp, 64, 16, WHITE);
else if (m_mode == MODE_YSF)
display.print("Fusion");
else if (m_mode == MODE_P25)

60
OLED.h
View file

@ -34,48 +34,10 @@
#include "Adafruit_GFX.h"
#include "ArduiPi_OLED.h"
static unsigned char logo_glcd_bmp[] =
{ 0b00101011, 0b11010100,
0b01010111, 0b11101010,
0b01010111, 0b11101010,
0b00101011, 0b11010100,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000000, 0b00000000 };
//DMR 48x16 px
static unsigned char logo_dmr_bmp[] =
{ 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b10111111, 0b11111000, 0b01111000, 0b00011110, 0b01111111, 0b11100001,
0b10111111, 0b11111110, 0b01111100, 0b00111110, 0b01100000, 0b00011001,
0b10110000, 0b00001110, 0b01100110, 0b01100110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100011, 0b11000110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100001, 0b10000110, 0b01100000, 0b00011001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01111111, 0b11111001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01111000, 0b00000001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01101100, 0b00000001,
0b10110000, 0b00000110, 0b01100000, 0b00000110, 0b01100110, 0b00000001,
0b10110000, 0b00001110, 0b01100000, 0b00000110, 0b01100011, 0b00000001,
0b10111111, 0b11111110, 0b01100000, 0b00000110, 0b01100001, 0b10000001,
0b10011111, 0b11111000, 0b01100000, 0b00000110, 0b01100000, 0b11000001,
0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111
};
class COLED : public CDisplay
{
public:
COLED(unsigned char displayType, unsigned char displayBrighness, unsigned char displayInvert);
COLED(unsigned char displayType, unsigned char displayBrighness, bool displayInvert);
virtual ~COLED();
virtual bool open();
@ -94,19 +56,21 @@ public:
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void close();
private:
const char* m_slot1_state;
const char* m_slot2_state;
unsigned char m_mode;
unsigned char m_displayType;
unsigned char m_displayBrightness;
unsigned char m_displayInvert;
const char* m_slot1_state;
const char* m_slot2_state;
unsigned char m_mode;
unsigned char m_displayType;
unsigned char m_displayBrightness;
bool m_displayInvert;
ArduiPi_OLED display;
void OLED_statusbar();

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -35,7 +35,7 @@ const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
CP25Control::CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup) :
CP25Control::CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper) :
m_nac(nac),
m_network(network),
m_display(display),
@ -63,10 +63,17 @@ m_netLDU1(NULL),
m_netLDU2(NULL),
m_lastIMBE(NULL),
m_rfLDU(NULL),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(lookup != NULL);
assert(rssiMapper != NULL);
m_netLDU1 = new unsigned char[9U * 25U];
m_netLDU2 = new unsigned char[9U * 25U];
@ -101,9 +108,14 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
return false;
if (data[0U] == TAG_LOST) {
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_netState == RS_NET_IDLE)
m_display->clearP25();
writeNetwork(m_rfLDU, m_lastDUID, true);
writeNetwork(data + 2U, P25_DUID_TERM, true);
m_rfState = RS_RF_LISTENING;
@ -139,6 +151,28 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
}
}
// Have we got RSSI bytes on the end of a P25 LDU?
if (len == (P25_LDU_FRAME_LENGTH_BYTES + 4U)) {
uint16_t raw = 0U;
raw |= (data[218U] << 8) & 0xFF00U;
raw |= (data[219U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("P25, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
if (duid == P25_DUID_LDU1) {
if (m_rfState == RS_RF_LISTENING) {
m_rfData.reset();
@ -148,6 +182,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
return false;
}
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
createRFHeader();
writeNetwork(data + 2U, P25_DUID_HEADER, false);
} else {
@ -168,7 +207,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
// Regenerate Audio
unsigned int errors = m_audio.process(data + 2U);
LogDebug("P25, LDU1 audio, errs: %u/1233", errors);
LogDebug("P25, LDU1 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F);
m_display->writeP25BER(float(errors) / 12.33F);
m_rfBits += 1233U;
m_rfErrs += errors;
@ -199,6 +240,8 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
m_display->writeP25(source.c_str(), grp, dstId, "R");
m_rfState = RS_RF_AUDIO;
}
m_display->writeP25RSSI(m_rssi);
} else if (duid == P25_DUID_LDU2) {
if (m_rfState == RS_RF_LISTENING)
return false;
@ -219,7 +262,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
// Regenerate Audio
unsigned int errors = m_audio.process(data + 2U);
LogDebug("P25, LDU2 audio, errs: %u/1233", errors);
LogDebug("P25, LDU2 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F);
m_display->writeP25BER(float(errors) / 12.33F);
m_rfBits += 1233U;
m_rfErrs += errors;
@ -240,6 +285,8 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
data[1U] = 0x00U;
writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
}
m_display->writeP25RSSI(m_rssi);
} else if (duid == P25_DUID_TERM || duid == P25_DUID_TERM_LC) {
if (m_rfState == RS_RF_LISTENING)
return false;
@ -262,7 +309,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
m_rfData.reset();
m_lastDUID = duid;
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
m_display->clearP25();
#if defined(DUMP_P25)
@ -391,6 +442,9 @@ void CP25Control::writeNetwork()
}
createNetLDU2();
break;
case 0x80U:
createNetTerminator();
break;
default:
break;
}
@ -468,9 +522,6 @@ void CP25Control::writeNetwork(const unsigned char *data, unsigned char type, bo
switch (type)
{
case P25_DUID_HEADER:
m_network->writeHeader();
break;
case P25_DUID_LDU1:
m_network->writeLDU1(data, m_rfData, m_rfLSD, end);
break;
@ -718,10 +769,6 @@ void CP25Control::createNetLDU1()
writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
// Check for an end of stream marker
if (m_netLDU1[200U] == 0x6AU && m_netLDU1[215U] == 0x00U)
createNetTerminator();
::memset(m_netLDU1, 0x00U, 9U * 25U);
m_netFrames += 9U;
@ -767,10 +814,6 @@ void CP25Control::createNetLDU2()
writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
// Check for an end of stream marker
if (m_netLDU2[200U] == 0x73U && m_netLDU2[215U] == 0x00U)
createNetTerminator();
::memset(m_netLDU2, 0x00U, 9U * 25U);
m_netFrames += 9U;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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,7 @@
#if !defined(P25Control_H)
#define P25Control_H
#include "RSSIInterpolator.h"
#include "P25LowSpeedData.h"
#include "RingBuffer.h"
#include "P25Network.h"
@ -35,7 +36,7 @@
class CP25Control {
public:
CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup);
CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper);
~CP25Control();
bool writeModem(unsigned char* data, unsigned int len);
@ -45,34 +46,40 @@ public:
void clock(unsigned int ms);
private:
unsigned int m_nac;
CP25Network* m_network;
CDisplay* m_display;
bool m_duplex;
CDMRLookup* m_lookup;
unsigned int m_nac;
CP25Network* m_network;
CDisplay* m_display;
bool m_duplex;
CDMRLookup* m_lookup;
CRingBuffer<unsigned char> m_queue;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CTimer m_rfTimeout;
CTimer m_netTimeout;
CTimer m_networkWatchdog;
unsigned int m_rfFrames;
unsigned int m_rfBits;
unsigned int m_rfErrs;
unsigned int m_netFrames;
unsigned int m_netLost;
CP25NID m_nid;
unsigned char m_lastDUID;
CP25Audio m_audio;
CP25Data m_rfData;
CP25Data m_netData;
CP25LowSpeedData m_rfLSD;
CP25LowSpeedData m_netLSD;
unsigned char* m_netLDU1;
unsigned char* m_netLDU2;
unsigned char* m_lastIMBE;
unsigned char* m_rfLDU;
FILE* m_fp;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CTimer m_rfTimeout;
CTimer m_netTimeout;
CTimer m_networkWatchdog;
unsigned int m_rfFrames;
unsigned int m_rfBits;
unsigned int m_rfErrs;
unsigned int m_netFrames;
unsigned int m_netLost;
CP25NID m_nid;
unsigned char m_lastDUID;
CP25Audio m_audio;
CP25Data m_rfData;
CP25Data m_netData;
CP25LowSpeedData m_rfLSD;
CP25LowSpeedData m_netLSD;
unsigned char* m_netLDU1;
unsigned char* m_netLDU2;
unsigned char* m_lastIMBE;
unsigned char* m_rfLDU;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeQueueRF(const unsigned char* data, unsigned int length);
void writeQueueNet(const unsigned char* data, unsigned int length);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 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
@ -96,23 +96,32 @@ bool CP25Data::decodeLDU1(const unsigned char* data)
CP25Utils::decode(data, raw, 1356U, 1398U);
decodeLDUHamming(raw, rs + 15U);
bool ret = m_rs241213.decode(rs);
if (!ret)
try {
bool ret = m_rs241213.decode(rs);
if (!ret)
return false;
} catch (...) {
CUtils::dump(2U, "P25, RS carshed with input data", rs, 18U);
return false;
}
// Simple validation of the source id
unsigned int srcId = (rs[6U] << 16) + (rs[7U] << 8) + rs[8U];
if (srcId < 1000000U)
return false;
switch (m_lcf) {
switch (rs[0U]) {
case P25_LCF_GROUP:
m_emergency = (rs[2U] & 0x80U) == 0x80U;
m_dstId = (rs[4U] << 8) + rs[5U];
m_srcId = (rs[6U] << 16) + (rs[7U] << 8) + rs[8U];
m_srcId = srcId;
break;
case P25_LCF_PRIVATE:
m_emergency = false;
m_dstId = (rs[3U] << 16) + (rs[4U] << 8) + rs[5U];
m_srcId = (rs[6U] << 16) + (rs[7U] << 8) + rs[8U];
m_srcId = srcId;
break;
default:
LogMessage("P25, unknown LCF value in LDU1 - $%02X", m_lcf);
return false;
}

View file

@ -26,24 +26,64 @@
#include <cassert>
#include <cstring>
const unsigned char STARTICW[] = {
0x00U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x35U, 0xA3U, 0x00U, 0x00U, 0x00U};
const unsigned char VHDR1[] = {
0x60U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x35U, 0xA3U, 0x1AU, 0x65U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x08U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, 0x2EU};
const unsigned char VHDR2[] = {
0x61U, 0x1DU, 0x35U, 0x34U, 0x37U, 0x0AU, 0x22U, 0x35U, 0x3EU, 0x00U, 0x10U, 0x02U, 0x11U, 0x28U, 0x1DU, 0x21U,
0x03U, 0x1BU, 0x00U, 0x35U, 0x23U, 0x02U};
const unsigned char REC62[] = {
0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x35U, 0xA3U, 0x1AU, 0x65U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x1BU, 0x5AU, 0x1AU, 0x2BU, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC63[] = {
0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x7AU};
const unsigned char REC64[] = {
0x64U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC65[] = {
0x65U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC66[] = {
0x66U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC67[] = {
0x67U, 0xC4U, 0x52U, 0x9BU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC68[] = {
0x68U, 0x9AU, 0xECU, 0xBAU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC69[] = {
0x69U, 0xB9U, 0xD8U, 0x16U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6A[] = {
0x6AU, 0x00U, 0x00U, 0x06U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6B[] = {
0x6BU, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x35U, 0xA3U, 0x1AU, 0x64U, 0x9CU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U };
0x6BU, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x1BU, 0x5AU, 0x1AU, 0x2BU, 0xACU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6C[] = {
0x6CU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xD6U};
const unsigned char REC6D[] = {
0x6DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6E[] = {
0x6EU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6F[] = {
0x6FU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC70[] = {
0x70U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC71[] = {
0x71U, 0xACU, 0xB8U, 0xA4U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC72[] = {
0x72U, 0x9BU, 0xDCU, 0x75U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC73[] = {
0x73U, 0x00U, 0x00U, 0x06U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC80[] = {
0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned int BUFFER_LENGTH = 100U;
@ -73,32 +113,6 @@ bool CP25Network::open()
return m_socket.open();
}
bool CP25Network::writeHeader()
{
if (m_debug)
CUtils::dump(1U, "P25 Network ICW Sent", STARTICW, 10U);
bool ret = m_socket.write(STARTICW, 10U, m_address, m_port);
if (!ret)
return false;
if (m_debug)
CUtils::dump(1U, "P25 Network VHDR1 Sent", VHDR1, 30U);
ret = m_socket.write(VHDR1, 30U, m_address, m_port);
if (!ret)
return false;
if (m_debug)
CUtils::dump(1U, "P25 Network VHDR2 Sent", VHDR2, 22U);
ret = m_socket.write(VHDR2, 22U, m_address, m_port);
if (!ret)
return false;
return true;
}
bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control, const CP25LowSpeedData& lsd, bool end)
{
assert(ldu1 != NULL);
@ -117,10 +131,8 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '63' record
::memset(buffer, 0x00U, 14U);
buffer[0U] = 0x63U;
::memcpy(buffer, REC63, 14U);
m_audio.decode(ldu1, buffer + 1U, 1U);
buffer[13U] = 0x7AU;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 14U);
@ -130,12 +142,10 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '64' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x64U;
::memcpy(buffer, REC64, 17U);
buffer[1U] = control.getLCF();
buffer[2U] = control.getMFId();
m_audio.decode(ldu1, buffer + 5U, 2U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -145,14 +155,12 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '65' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x65U;
::memcpy(buffer, REC65, 17U);
unsigned int id = control.getDstId();
buffer[1U] = (id >> 16) & 0xFFU;
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu1, buffer + 5U, 3U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -162,14 +170,12 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '66' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x66U;
::memcpy(buffer, REC66, 17U);
id = control.getSrcId();
buffer[1U] = (id >> 16) & 0xFFU;
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu1, buffer + 5U, 4U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -179,13 +185,8 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '67' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x67U;
buffer[1U] = 0x28U; // XXX ???
buffer[2U] = 0xD6U; // XXX ???
buffer[3U] = 0x58U; // XXX ???
::memcpy(buffer, REC67, 17U);
m_audio.decode(ldu1, buffer + 5U, 5U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -195,13 +196,8 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '68' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x68U;
buffer[1U] = 0xA0U; // XXX ???
buffer[2U] = 0x81U; // XXX ???
buffer[3U] = 0x9CU; // XXX ???
::memcpy(buffer, REC68, 17U);
m_audio.decode(ldu1, buffer + 5U, 6U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -211,13 +207,8 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '69' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x69U;
buffer[1U] = 0x0EU; // XXX ???
buffer[2U] = 0x74U; // XXX ???
buffer[3U] = 0xBCU; // XXX ???
::memcpy(buffer, REC69, 17U);
m_audio.decode(ldu1, buffer + 5U, 7U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
@ -227,13 +218,10 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
return false;
// The '6A' record
::memset(buffer, 0x00U, 16U);
buffer[0U] = 0x6AU;
::memcpy(buffer, REC6A, 16U);
buffer[1U] = lsd.getLSD1();
buffer[2U] = lsd.getLSD2();
buffer[3U] = 0x0EU; // XXX ???
m_audio.decode(ldu1, buffer + 4U, 8U);
buffer[15U] = end ? 0x00U : 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 16U);
@ -242,6 +230,15 @@ bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control,
if (!ret)
return false;
if (end) {
if (m_debug)
CUtils::dump(1U, "P25 Network END Sent", REC80, 17U);
ret = m_socket.write(REC80, 17U, m_address, m_port);
if (!ret)
return false;
}
return true;
}
@ -263,11 +260,8 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '6C' record
::memset(buffer, 0x00U, 14U);
buffer[0U] = 0x6CU;
::memcpy(buffer, REC6C, 14U);
m_audio.decode(ldu2, buffer + 1U, 1U);
buffer[12U] = 0x02U;
buffer[13U] = 0xF6U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 14U);
@ -280,13 +274,11 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
control.getMI(mi);
// The '6D' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x6DU;
::memcpy(buffer, REC6D, 17U);
buffer[1U] = mi[0U];
buffer[2U] = mi[1U];
buffer[3U] = mi[2U];
m_audio.decode(ldu2, buffer + 5U, 2U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -296,13 +288,11 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '6E' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x6EU;
::memcpy(buffer, REC6E, 17U);
buffer[1U] = mi[3U];
buffer[2U] = mi[4U];
buffer[3U] = mi[5U];
m_audio.decode(ldu2, buffer + 5U, 3U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -312,13 +302,11 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '6F' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x6FU;
::memcpy(buffer, REC6F, 17U);
buffer[1U] = mi[6U];
buffer[2U] = mi[7U];
buffer[3U] = mi[8U];
m_audio.decode(ldu2, buffer + 5U, 4U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -328,14 +316,12 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '70' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x70U;
::memcpy(buffer, REC70, 17U);
buffer[1U] = control.getAlgId();
unsigned int id = control.getKId();
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu2, buffer + 5U, 5U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -345,13 +331,8 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '71' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x71U;
buffer[1U] = 0xACU; // XXX ???
buffer[2U] = 0xB8U; // XXX ???
buffer[3U] = 0xA4U; // XXX ???
::memcpy(buffer, REC71, 17U);
m_audio.decode(ldu2, buffer + 5U, 6U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -361,13 +342,8 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '72' record
::memset(buffer, 0x00U, 17U);
buffer[0U] = 0x72U;
buffer[1U] = 0x9BU; // XXX ???
buffer[2U] = 0xDCU; // XXX ???
buffer[3U] = 0x75U; // XXX ???
::memcpy(buffer, REC72, 17U);
m_audio.decode(ldu2, buffer + 5U, 7U);
buffer[16U] = 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
@ -377,13 +353,10 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
return false;
// The '73' record
::memset(buffer, 0x00U, 16U);
buffer[0U] = 0x73U;
::memcpy(buffer, REC73, 16U);
buffer[1U] = lsd.getLSD1();
buffer[2U] = lsd.getLSD2();
buffer[3U] = 0x06U; // XXX ???
m_audio.decode(ldu2, buffer + 4U, 8U);
buffer[15U] = end ? 0x00U : 0x02U;
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 16U);
@ -392,6 +365,15 @@ bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control,
if (!ret)
return false;
if (end) {
if (m_debug)
CUtils::dump(1U, "P25 Network END Sent", REC80, 17U);
ret = m_socket.write(REC80, 17U, m_address, m_port);
if (!ret)
return false;
}
return true;
}

View file

@ -37,8 +37,6 @@ public:
void enable(bool enabled);
bool writeHeader();
bool writeLDU1(const unsigned char* ldu1, const CP25Data& control, const CP25LowSpeedData& lsd, bool end);
bool writeLDU2(const unsigned char* ldu2, const CP25Data& control, const CP25LowSpeedData& lsd, bool end);

View file

@ -1,32 +0,0 @@
To use DMR Access Control you can add the following fields to your MMDVM.ini:
Blacklist= <comma separated list of source-id's to block on RF>
DstIdBlackListSlot1RF= <comma separated list TGs to block on RF Slot1>
DstIdBlackListSlot2RF= <comma separated list TGs to block on RF Slot2>
DstIdWhiteListSlot1RF= <comma separated list TGs to allow on RF Slot1>
DstIdWhiteListSlot2RF= <comma separated list TGs to allow on RF Slot2>
DstIdBlackListSlot1NET= <comma separated list TGs to block on NET Slot1>
DstIdBlackListSlot2NET= <comma separated list TGs to block on NET Slot2>
DstIdWhiteListSlot1NET= <comma separated list TGs to allow on NET Slot1>
DstIdWhiteListSlot2NET= <comma separated list TGs to allow on NET Slot2>
So, for example:
DstIdBlackListSlot1=91 - block the TG91 net.
DstIdWhiteListSlot1=9.5057,9990 - allows TG9, APRS SMS Gateway and Echo.
If the whitelist is null and commented out, it is disabled.
The whitelist behaves slightly differently on Slot1 than is does on Slot2.
For Slot1, the whitelist will allow all IDs above 99999 and everything in the whitelist.
For Slot2, the whitelist will allow all IDs between 4000 and 5000, IDs above 99999 and everything in the whitelist.
You can use the blacklist with the whitelist if you want to specifically block IDs within the allowed ranges above.
For example, to block users from disconnecting the reflectors, you could block ID 4000.
To block users connecting to reflector 4400 you could add ID 4400 to the network blacklist for that slot.

View file

@ -6,11 +6,49 @@ http://wiringpi.com/download-and-install/ which must be installed in all cases.
The HD44780 in 4-bit mode is probably the most common connection method and
wiring details can be found at http://wiringpi.com/dev-lib/lcd-library/
The setup that is commonly used for MMDVM is the 4-bit connection that is also
documented within wiringPi. The HD44780 displays have a standardized 16-pin
connector (14 pins if without backlight). The pin numbers on HD44780 refer to
this standard numbering.
The pin numbers on Raspberry Pi side are the physical pin numbers of the GPIO
pin header, in brackets the wiringPi pin number as these are referred to in the
MMDVM.ini file.
The wiring ist as follows:
HD44780 pin Raspberry Pi GPIO
GND 1 -------- 6 GND
VCC 2 -------- 2 +5V
V0 3 -------- Trimmer between +5V and GND for contrast
RS 4 -------- 26 CE1 (11)
RW 5 -------- 6 GND
E 6 -------- 24 CE0 (10)
D0 7
D1 8
D2 9
D3 10
D4 11 -------- 11 GPIO0 (0)
D5 12 -------- 12 GPIO1 (1)
D6 13 -------- 13 GPIO2 (2)
D7 14 -------- 15 GPIO3 (3)
VCC 15 -------- 2 +5V
GND 16 -------- 6 GND
The relevant part in the MMDVM.ini is like outlined below.
[HD44780]
Rows=4
Columns=20
# For basic HD44780 displays (4-bit connection)
# rs, strb, d0, d1, d2, d3
Pins=11,10,0,1,2,3
To compile MMDVMHost with support for the HD44780 use the following commands.
# cp Makefile.Pi.HD44780 Makefile
# make clean
# make
# make -f Makefile.Pi.HD44780
Other HD44780 variations exist that connect via I2C. Support is also via
wiringPi, but to compile for each I2C device requires a different Makefile

View file

@ -1,57 +0,0 @@
Talk-Group Rewrite was conceived as a way of making talk-groups behave more
like the reflector system and of attempting to solve the problem of "slot
contention",where the user may be locked out of a slot by traffic on a
talk-group they know nothing about, without knowing why. This is frustrating to
users, both those new to DMR and seasoned DMR users.
TG Rewrite, when enabled for a slot, rewrites the DST ID of incoming talkgroup
traffic to TG9, alowing audio to be heard by any user monitoring TG9 on that
slot. If the user then replies on TG9, as long as they key-up during the
CallHang period, the DST ID (TG) is again rewritten on the outbound traffic,
which transparently maps back to the originating talkgroup. Rewrite is also
enabled if an outbound call is made to a talkgroup and then TG9 is activated
during the CallHang period.
To use a User Activated talk-group, either briefly key-up on that talk-group to
activate it, then switch back to TG9 and talk as normal. Alternatively, and
perhaps more usefully, you can make a private call to the talk-group ID. This
enables you to manual-dial a talkgroup that is not in your codeplug, in the same
way that you can manual dial a reflector. If the CallHang period
expires, you may need to activate the talk-group again by keying up on that
talkgroup briefly, as before. Alternatively, you can wait for inbound audio to
re-activate the rewrite, then respond during the CallHang period.
Note, proper ettiquette dictates that the user confirms the repeater is free
before activiating this feature. It is best to ensure the slot is keyed from
"cold" when activating a talkgroup for rewrite. Although not strictly
neccesary, it may be advantageous to also disconnect the repeater from the
reflector system first, if using slot 2.
Talk-group rewrite was originally intended to work with User-Activated talk
groups, although it will function with permanent talk-groups too.
It is useful to set the CallHang parameter to a generous amount. I am currently
using seven seconds.
In addition to the above, there is also the capability ro rewrite voice prompts
on link/unlink so all users can hear them on TG9.
Configuration options are as follows:
TGRewriteSlot1=[0|1]
TGRewriteSlot2=[0|1]
BMAutoRewrite=[0|1]
BMRewriteReflectorVoicePrompts=[0|1]
Note at present, Auto rewrite is the only type of rewrite that is implemented
so
to do something useful, this option must be set.
ACL's are applied before the rewrite, so still apply to rewritten traffic on
original (non-rewritten) talk-group.
73
Simon (G7RZU)

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. On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it connects to Brand Meister, DMR+, and HB Link, on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25NX network.
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, and System Fusion.
It supports D-Star, DMR, P25 Phase 1, and System Fusion.
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, or HB Link, on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25 Gateway.
It builds on 32-bit and 64-bit Linux as well as on Windows using VS2015 on x86 and x64. It can optionally control various Displays. Currently these are:
@ -9,11 +9,15 @@ It builds on 32-bit and 64-bit Linux as well as on Windows using VS2015 on x86 a
- Adafruit 16x2 LCD+Keypad Kits (I2C)
- Connection via PCF8574 GPIO Extender (I2C)
- Nextion TFTs (sizes 2.4", 2.8", 3.2" and 3.5")
- TFT displays sold by Hobbytronics in UK
- TFT display sold by Hobbytronics in UK
- OLED 128x64 (SSD1306)
The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms. The other displays can be directly connected to the UART on Raspberry Pis or with FT-232RL modules to any USB port.
The Nextion displays can connect to the UART on the Raspberry Pi, or via a USB to TTL serial converter like the FT-232RL. It may also be connected to the UART output of the MMDVM modem (Arduino Due, STM32, Teensy), or to the UART output on the UMP.
The OLED display needs a extra lib see OLED.md
The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms.
The Hobbytronics TFT Display, which is a Pi-Hat, connects to the UART on the Raspbery Pi.
The OLED display needs a extra library see OLED.md
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.

11
RSSI.dat Normal file
View file

@ -0,0 +1,11 @@
# This file maps the raw RSSI values to dBm values to send to the DMR network. A number of data
# points should be entered and the software will use those to work out the in-between values.
#
# The format of the file is:
# Raw RSSI Value dBm Value
#
# For example
# 1134 -90
# 1123 -100
# 1000 -109
#

42
RSSI/RSSI_GM340.dat Normal file
View file

@ -0,0 +1,42 @@
# This file maps the raw RSSI values to dBm values to send to the DMR network. A number of data
# points should be entered and the software will use those to work out the in-between values.
#
# The format of the file is:
# Raw RSSI Value dBm Value
#
# Measured with a Marconi 2955 Test set and Motorola GM340 UHF (430.6125 MHz).
# Both S-meter (6dB) and decade (10dB) steps are included between the noise floor and saturation.
# George M1GEO / GB7KH - 01/01/2017
#
2841 -20
2848 -30
2854 -40
2858 -43
2858 -48
2853 -50
2845 -53
2780 -58
2732 -60
2647 -63
2484 -68
2422 -70
2356 -73
2234 -78
2183 -80
2103 -83
1978 -88
1931 -90
1875 -93
1710 -99
1686 -100
1545 -105
1416 -110
1398 -111
1238 -117
1166 -120
1106 -123
1018 -129
1006 -130
985 -135
976 -140
971 -150

48
RSSI/RSSI_TB7100.dat Normal file
View file

@ -0,0 +1,48 @@
# This file maps the raw RSSI values to dBm values to send to the DMR network. A number of data
# points should be entered and the software will use those to work out the in-between values.
#
# The format of the file is:
# Raw RSSI Value dBm Value #output voltage
#
# The following values were measured with a Marconi 2024 signal generator.
# Setup is MMDVM (PCB by Toufik, F0DEI) on a STM32F446RE Nucleo board.
# The values in the comments are the voltage measured at the TB7100 RSSI output pin.
# Florian DF2ET, 03.03.2017
#
2303 -22 #2.93
2256 -25 #2.86
2184 -28 #2.77
2159 -31 #2.74
2095 -34 #2.66
2040 -37 #2.59
1985 -40 #2.52
1922 -43 #2.44
1868 -46 #2.37
1804 -49 #2.29
1758 -52 #2.23
1694 -55 #2.15
1640 -58 #2.08
1568 -61 #1.99
1540 -64 #1.95
1459 -67 #1.85
1403 -70 #1.78
1340 -73 #1.70
1287 -76 #1.63
1233 -79 #1.56
1178 -82 #1.49
1116 -85 #1.41
1054 -88 #1.33
1000 -91 #1.27
945 -94 #1.20
890 -97 #1.13
830 -100 #1.05
768 -103 #0.97
715 -106 #0.90
660 -109 #0.83
600 -112 #0.76
540 -115 #0.68
500 -118 #0.63
450 -121 #0.57
420 -124 #0.52
390 -127 #0.48
370 -130 #0.47

91
RSSIInterpolator.cpp Normal file
View file

@ -0,0 +1,91 @@
/*
* Copyright (C) 2016 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 "RSSIInterpolator.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
CRSSIInterpolator::CRSSIInterpolator() :
m_map()
{
}
CRSSIInterpolator::~CRSSIInterpolator()
{
m_map.clear();
}
bool CRSSIInterpolator::load(const std::string& filename)
{
FILE* fp = ::fopen(filename.c_str(), "rt");
if (fp == NULL) {
LogWarning("Cannot open the RSSI data file - %s", filename.c_str());
return false;
}
char buffer[100U];
while (::fgets(buffer, 100, fp) != NULL) {
if (buffer[0U] == '#')
continue;
char* p1 = ::strtok(buffer, " \t\r\n");
char* p2 = ::strtok(NULL, " \t\r\n");
if (p1 != NULL && p2 != NULL) {
uint16_t raw = uint16_t(::atoi(p1));
int rssi = ::atoi(p2);
m_map.insert(std::pair<uint16_t, int>(raw, rssi));
}
}
::fclose(fp);
LogInfo("Loaded %u RSSI data mapping points from %s", m_map.size(), filename.c_str());
return true;
}
int CRSSIInterpolator::interpolate(uint16_t val) const
{
if (m_map.empty())
return 0;
std::map<uint16_t, int>::const_iterator it = m_map.lower_bound(val);
if (it == m_map.end())
return m_map.rbegin()->second;
if (it == m_map.begin())
return it->second;
uint16_t x2 = it->first;
int y2 = it->second;
--it;
uint16_t x1 = it->first;
int y1 = it->second;
float p = float(val - x1) / float(x2 - x1);
return int((1.0F - p) * float(y1) + p * float(y2));
}

39
RSSIInterpolator.h Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 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(RSSIINTERPOLATOR_H)
#define RSSIINTERPOLATOR_H
#include <cstdint>
#include <string>
#include <map>
class CRSSIInterpolator {
public:
CRSSIInterpolator();
~CRSSIInterpolator();
bool load(const std::string& filename);
int interpolate(uint16_t raw) const;
private:
std::map<uint16_t, int> m_map;
};
#endif

283
UMP.cpp Normal file
View file

@ -0,0 +1,283 @@
/*
* Copyright (C) 2016 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 "Defines.h"
#include "Utils.h"
#include "Log.h"
#include "UMP.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned char UMP_FRAME_START = 0xF0U;
const unsigned char UMP_HELLO = 0x00U;
const unsigned char UMP_SET_MODE = 0x01U;
const unsigned char UMP_SET_TX = 0x02U;
const unsigned char UMP_SET_CD = 0x03U;
const unsigned char UMP_WRITE_SERIAL = 0x10U;
const unsigned char UMP_READ_SERIAL = 0x11U;
const unsigned char UMP_STATUS = 0x50U;
const unsigned int BUFFER_LENGTH = 255U;
CUMP::CUMP(const std::string& port) :
m_serial(port, SERIAL_115200),
m_open(false),
m_buffer(NULL),
m_length(0U),
m_offset(0U),
m_lockout(false),
m_mode(MODE_IDLE),
m_tx(false),
m_cd(false)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CUMP::~CUMP()
{
delete[] m_buffer;
}
bool CUMP::open()
{
if (m_open)
return true;
LogMessage("Opening the UMP");
bool ret = m_serial.open();
if (!ret)
return false;
unsigned char buffer[3U];
buffer[0U] = UMP_FRAME_START;
buffer[1U] = 3U;
buffer[2U] = UMP_HELLO;
// CUtils::dump(1U, "Transmitted", buffer, 3U);
int n = m_serial.write(buffer, 3U);
if (n != 3) {
m_serial.close();
return false;
}
m_open = true;
return true;
}
bool CUMP::setMode(unsigned char mode)
{
if (mode == m_mode)
return true;
m_mode = mode;
unsigned char buffer[4U];
buffer[0U] = UMP_FRAME_START;
buffer[1U] = 4U;
buffer[2U] = UMP_SET_MODE;
buffer[3U] = mode;
// CUtils::dump(1U, "Transmitted", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
}
bool CUMP::setTX(bool on)
{
if (on == m_tx)
return true;
m_tx = on;
unsigned char buffer[4U];
buffer[0U] = UMP_FRAME_START;
buffer[1U] = 4U;
buffer[2U] = UMP_SET_TX;
buffer[3U] = on ? 0x01U : 0x00U;
// CUtils::dump(1U, "Transmitted", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
}
bool CUMP::setCD(bool on)
{
if (on == m_cd)
return true;
m_cd = on;
unsigned char buffer[4U];
buffer[0U] = UMP_FRAME_START;
buffer[1U] = 4U;
buffer[2U] = UMP_SET_CD;
buffer[3U] = on ? 0x01U : 0x00U;
// CUtils::dump(1U, "Transmitted", buffer, 4U);
return m_serial.write(buffer, 4U) == 4;
}
bool CUMP::getLockout() const
{
return m_lockout;
}
int CUMP::write(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
unsigned char buffer[250U];
buffer[0U] = UMP_FRAME_START;
buffer[1U] = length + 3U;
buffer[2U] = UMP_WRITE_SERIAL;
::memcpy(buffer + 3U, data, length);
// CUtils::dump(1U, "Transmitted", buffer, length + 3U);
return m_serial.write(buffer, length + 3U);
}
// To be implemented later if needed
int CUMP::read(unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
return 0;
}
void CUMP::clock(unsigned int ms)
{
if (m_offset == 0U) {
// Get the start of the frame or nothing at all
int ret = m_serial.read(m_buffer + 0U, 1U);
if (ret < 0) {
LogError("Error when reading from the UMP");
return;
}
if (ret == 0)
return;
if (m_buffer[0U] != UMP_FRAME_START)
return;
m_offset = 1U;
}
if (m_offset == 1U) {
// Get the length of the frame
int ret = m_serial.read(m_buffer + 1U, 1U);
if (ret < 0) {
LogError("Error when reading from the UMP");
m_offset = 0U;
return;
}
if (ret == 0)
return;
if (m_buffer[1U] >= 250U) {
LogError("Invalid length received from the UMP - %u", m_buffer[1U]);
m_offset = 0U;
return;
}
m_length = m_buffer[1U];
m_offset = 2U;
}
if (m_offset == 2U) {
// Get the frame type
int ret = m_serial.read(m_buffer + 2U, 1U);
if (ret < 0) {
LogError("Error when reading from the UMP");
m_offset = 0U;
return;
}
if (ret == 0)
return;
switch (m_buffer[2U]) {
case UMP_STATUS:
case UMP_READ_SERIAL:
break;
default:
LogError("Unknown message, type: %02X", m_buffer[2U]);
m_offset = 0U;
return;
}
m_offset = 3U;
}
if (m_offset >= 3U) {
while (m_offset < m_length) {
int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset);
if (ret < 0) {
LogError("Error when reading from the UMP");
m_offset = 0U;
return;
}
if (ret == 0)
return;
if (ret > 0)
m_offset += ret;
}
}
m_offset = 0U;
// CUtils::dump(1U, "Received", m_buffer, m_length);
if (m_buffer[2U] == UMP_STATUS)
m_lockout = (m_buffer[3U] & 0x01U) == 0x01U;
}
void CUMP::close()
{
if (!m_open)
return;
LogMessage("Closing the UMP");
m_serial.close();
m_open = false;
}

63
UMP.h Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 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(UMP_H)
#define UMP_H
#include "SerialController.h"
#include "SerialPort.h"
#include <string>
class CUMP : public ISerialPort
{
public:
CUMP(const std::string& port);
virtual ~CUMP();
virtual bool open();
bool setMode(unsigned char mode);
bool setTX(bool on);
bool setCD(bool on);
bool getLockout() const;
virtual int read(unsigned char* buffer, unsigned int length);
virtual int write(const unsigned char* buffer, unsigned int length);
void clock(unsigned int ms);
virtual void close();
private:
CSerialController m_serial;
bool m_open;
unsigned char* m_buffer;
unsigned int m_length;
unsigned int m_offset;
bool m_lockout;
unsigned char m_mode;
bool m_tx;
bool m_cd;
};
#endif

203
UMP/UMP.ino Normal file
View file

@ -0,0 +1,203 @@
/*
* Copyright (C) 2016 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(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__)
#include <AltSoftSerial.h>
#endif
#if !defined(PIN_LED)
#define PIN_LED 13
#endif
#if defined(__MK20DX256__)
#define PIN_DSTAR 3
#define PIN_DMR 4
#define PIN_YSF 5
#define PIN_P25 6
#define PIN_TX 10
#define PIN_CD 11
#define PIN_LOCKOUT 12
#define FLASH_DELAY 200000U
#else
#define PIN_DSTAR 2
#define PIN_DMR 3
#define PIN_YSF 4
#define PIN_P25 5
#define PIN_TX 6
#define PIN_CD 7
#define PIN_LOCKOUT 12
#define FLASH_DELAY 3200U
#endif
#if !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__)
AltSoftSerial mySerial;
#endif
// Use the LOCKOUT function on the UMP
// #define USE_LOCKOUT
void setup()
{
Serial.begin(115200);
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
Serial1.begin(9600);
#else
mySerial.begin(9600);
#endif
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_DSTAR, OUTPUT);
pinMode(PIN_DMR, OUTPUT);
pinMode(PIN_YSF, OUTPUT);
pinMode(PIN_P25, 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_TX, LOW);
digitalWrite(PIN_CD, LOW);
}
#define UMP_FRAME_START 0xF0U
#define UMP_HELLO 0x00U
#define UMP_SET_MODE 0x01U
#define UMP_SET_TX 0x02U
#define UMP_SET_CD 0x03U
#define UMP_WRITE_SERIAL 0x10U
#define UMP_STATUS 0x50U
#define MODE_IDLE 0U
#define MODE_DSTAR 1U
#define MODE_DMR 2U
#define MODE_YSF 3U
#define MODE_P25 4U
bool m_started = false;
uint32_t m_count = 0U;
bool m_led = false;
uint8_t m_buffer[256U];
uint8_t m_offset = 0U;
uint8_t m_length = 0U;
bool m_lockout = false;
void loop()
{
while (Serial.available()) {
uint8_t c = Serial.read();
if (m_offset == 0U) {
if (c == UMP_FRAME_START) {
m_buffer[m_offset] = c;
m_offset = 1U;
}
} else if (m_offset == 1U) {
m_length = m_buffer[m_offset] = c;
m_offset = 2U;
} else {
m_buffer[m_offset] = c;
m_offset++;
if (m_length == m_offset) {
switch (m_buffer[2U]) {
case UMP_HELLO:
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);
break;
case UMP_SET_TX:
digitalWrite(PIN_TX, m_buffer[3U] == 0x01U ? HIGH : LOW);
break;
case UMP_SET_CD:
digitalWrite(PIN_CD, m_buffer[3U] == 0x01U ? HIGH : LOW);
break;
case UMP_WRITE_SERIAL:
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
Serial1.write(m_buffer + 3U, m_length - 3U);
#else
mySerial.write(m_buffer + 3U, m_length - 3U);
#endif
break;
default:
break;
}
m_length = 0U;
m_offset = 0U;
}
}
}
bool lockout = false;
#if defined(USE_LOCKOUT)
lockout = digitalRead(PIN_LOCKOUT) == HIGH;
#endif
if (lockout != m_lockout) {
uint8_t data[4U];
data[0U] = UMP_FRAME_START;
data[1U] = 4U;
data[2U] = UMP_STATUS;
data[3U] = lockout ? 0x01U : 0x00U;
Serial.write(data, 4U);
m_lockout = lockout;
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
while (Serial1.available())
Serial1.read();
#else
while (mySerial.available())
mySerial.read();
#endif
m_count++;
if (m_started) {
if (m_count > FLASH_DELAY) {
digitalWrite(PIN_LED, m_led ? LOW : HIGH);
m_led = !m_led;
m_count = 0U;
}
} else {
if (m_count > (FLASH_DELAY * 3U)) {
digitalWrite(PIN_LED, m_led ? LOW : HIGH);
m_led = !m_led;
m_count = 0U;
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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 = "20160906";
const char* VERSION = "20170303";
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 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
@ -24,7 +24,7 @@
// #define DUMP_YSF
CYSFControl::CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway) :
CYSFControl::CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) :
m_callsign(NULL),
m_network(network),
m_display(display),
@ -56,9 +56,16 @@ m_lastMR(YSF_MR_NOT_BUSY),
m_netN(0U),
m_rfPayload(),
m_netPayload(),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(rssiMapper != NULL);
m_rfPayload.setUplink(callsign);
m_rfPayload.setDownlink(callsign);
@ -94,7 +101,10 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
unsigned char type = data[0U];
if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) {
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
}
@ -102,6 +112,28 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
if (type == TAG_LOST)
return false;
// Have we got RSSI bytes on the end?
if (len == (YSF_FRAME_LENGTH_BYTES + 4U)) {
uint16_t raw = 0U;
raw |= (data[122U] << 8) & 0xFF00U;
raw |= (data[123U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
CYSFFICH fich;
bool valid = fich.decode(data + 2U);
@ -116,6 +148,11 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
m_rfTimeoutTimer.start();
m_rfPayload.reset();
m_rfState = RS_RF_AUDIO;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
#if defined(DUMP_YSF)
openFile();
#endif
@ -185,6 +222,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
}
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
} else if (valid && fi == YSF_FI_TERMINATOR) {
CSync::addYSFSync(data + 2U);
@ -209,7 +248,11 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
m_rfFrames++;
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
@ -230,6 +273,7 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
unsigned int errors = m_rfPayload.processVDMode1Audio(data + 2U);
m_rfErrs += errors;
m_rfBits += 235U;
m_display->writeFusionBER(float(errors) / 2.35F);
LogDebug("YSF, V/D Mode 1, seq %u, AMBE FEC %u/235 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.35F);
}
break;
@ -239,6 +283,7 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
unsigned int errors = m_rfPayload.processVDMode2Audio(data + 2U);
m_rfErrs += errors;
m_rfBits += 135U;
m_display->writeFusionBER(float(errors) / 1.35F);
LogDebug("YSF, V/D Mode 2, seq %u, Repetition FEC %u/135 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 1.35F);
}
break;
@ -254,6 +299,7 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
unsigned int errors = m_rfPayload.processVoiceFRModeAudio(data + 2U);
m_rfErrs += errors;
m_rfBits += 720U;
m_display->writeFusionBER(float(errors) / 7.2F);
LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F);
}
valid = false;
@ -314,6 +360,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
#endif
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
} else {
CSync::addYSFSync(data + 2U);
@ -329,6 +377,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
writeFile(data + 2U);
#endif
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
}
return true;
@ -466,8 +516,6 @@ void CYSFControl::writeNetwork()
CYSFFICH fich;
bool valid = fich.decode(data + 35U);
if (valid) {
unsigned char bn = fich.getBN();
unsigned char bt = fich.getBT();
unsigned char dt = fich.getDT();
unsigned char fn = fich.getFN();
unsigned char ft = fich.getFT();
@ -495,7 +543,6 @@ void CYSFControl::writeNetwork()
// if (send) {
m_netErrs += errors;
m_netBits += 235U;
LogDebug("YSF, V/D Mode 1, seq %u, AMBE FEC %u/235 (%.1f%%)", n, errors, float(errors) / 2.35F);
// }
}
break;
@ -507,13 +554,11 @@ void CYSFControl::writeNetwork()
// if (send) {
m_netErrs += errors;
m_netBits += 135U;
LogDebug("YSF, V/D Mode 2, seq %u, Repetition FEC %u/135 (%.1f%%)", n, errors, float(errors) / 1.35F);
// }
}
break;
case YSF_DT_DATA_FR_MODE:
LogDebug("YSF, Network data FICH B=%u/%u F=%u/%u", bn, bt, fn, ft);
m_netPayload.processDataFRModeData(data + 35U, fn, gateway);
break;
@ -525,7 +570,6 @@ void CYSFControl::writeNetwork()
// if (send) {
m_netErrs += errors;
m_netBits += 720U;
LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", n, errors, float(errors) / 7.2F);
// }
}
break;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 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,7 @@
#if !defined(YSFControl_H)
#define YSFControl_H
#include "RSSIInterpolator.h"
#include "YSFNetwork.h"
#include "YSFDefines.h"
#include "YSFPayload.h"
@ -33,7 +34,7 @@
class CYSFControl {
public:
CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway);
CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper);
~CYSFControl();
bool writeModem(unsigned char* data, unsigned int len);
@ -74,6 +75,12 @@ private:
unsigned char m_netN;
CYSFPayload m_rfPayload;
CYSFPayload m_netPayload;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeQueueRF(const unsigned char* data);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Mathias Weyland, HB9FRV
*
* This program is free software; you can redistribute it and/or modify
@ -397,12 +397,14 @@ unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data)
// We have a total of 5 VCH sections, iterate through each
for (unsigned int j = 0U; j < 5U; j++, offset += 144U) {
unsigned int errs = 0U;
unsigned char vch[13U];
// Deinterleave
for (unsigned int i = 0U; i < 104U; i++) {
unsigned int n = INTERLEAVE_TABLE_26_4[i];
bool s = READ_BIT1(data, offset + n) != 0x00U;
bool s = READ_BIT1(data, offset + n);
WRITE_BIT1(vch, i, s);
}
@ -413,19 +415,34 @@ unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data)
// errors += READ_BIT1(vch, 103); // Padding bit must be zero but apparently it is not...
for (unsigned int i = 0U; i < 81U; i += 3) {
uint8_t vote = bool(READ_BIT1(vch, i)) + bool(READ_BIT1(vch, i + 1)) + bool(READ_BIT1(vch, i + 2));
if (vote == 1 || vote == 2) {
bool decision = vote / 2; // exploit integer division: 1/2 == 0, 2/2 == 1.
WRITE_BIT1(vch, i, decision);
WRITE_BIT1(vch, i + 1, decision);
WRITE_BIT1(vch, i + 2, decision);
errors++;
uint8_t vote = 0U;
vote += READ_BIT1(vch, i + 0U) ? 1U : 0U;
vote += READ_BIT1(vch, i + 1U) ? 1U : 0U;
vote += READ_BIT1(vch, i + 2U) ? 1U : 0U;
switch (vote) {
case 1U: // 1 0 0, or 0 1 0, or 0 0 1, convert to 0 0 0
WRITE_BIT1(vch, i + 0U, false);
WRITE_BIT1(vch, i + 1U, false);
WRITE_BIT1(vch, i + 2U, false);
errs++;
break;
case 2U: // 1 1 0, or 0 1 1, or 1 0 1, convert to 1 1 1
WRITE_BIT1(vch, i + 0U, true);
WRITE_BIT1(vch, i + 1U, true);
WRITE_BIT1(vch, i + 2U, true);
errs++;
break;
default: // 0U (0 0 0), or 3U (1 1 1), no errors
break;
}
}
// Reconstruct only if we have bit errors. Technically we could even
// constrain it individually to the 5 VCH sections.
if (errors > 0U) {
// Reconstruct only if we have bit errors.
if (errs > 0U) {
// Accumulate the total number of errors
errors += errs;
// Scramble
for (unsigned int i = 0U; i < 13U; i++)
vch[i] ^= WHITENING_DATA[i];