pull/1/head
Tom Early 3 years ago
parent fd9ddd7181
commit cd30b7f771

@ -26,7 +26,7 @@ The rest of this README is unchanged from the original.
## Introduction
This will build a new kind of digital voice reflector. Based on N7TAE's [new-xlxd](https://github.com/n7tae/new-xlxd), which, in turn, is based on the first multi-protocol reflector, [xlxd](https://github.com/LX3JL/xlxd), **urfd** supports all protocols of it's predecessors, as well as both M17 protocols, **voice-only** and **voice+data**! A key part of this is the hybrid transcoder, [tcd](https://github.com/n7tae/tcd), which is in a seperate repository. URFd is not compatible with either new-xlxd or xlxd. You can't interlink urfd with xlxd. This reflector can be built without a transcoder, but clients will only hear other clients using the same protocol. Please note that currently, urfd only supports the tcd transcoder when run locally. For best performance, urfd and tcd uses UNIX DGRAM sockets for interprocess communications. These kernal-base sockets are signifantly faster than conventional UDP/IP sockets. It should be noted that tcd supports DVSI-3003 nad DVSI-3000 devices, which it uses for AMBE vocoding.
This will build a new kind of digital voice reflector. Based on N7TAE's [new-xlxd](https://github.com/n7tae/new-xlxd), which, in turn, is based on the first multi-protocol reflector, [xlxd](https://github.com/LX3JL/xlxd), **urfd** supports all protocols of it's predecessors, as well as both M17 protocols, **voice-only** and **voice+data**! A key part of this is the hybrid transcoder, [tcd](https://github.com/n7tae/tcd), which is in a seperate repository. URFd is not compatible with either new-xlxd or xlxd. You can't interlink urfd with xlxd. This reflector can be built without a transcoder, but clients will only hear other clients using the same protocol. Please note that currently, urfd only supports the tcd transcoder when run locally. For best performance, urfd and tcd uses UNIX DGRAM sockets for interprocess communications. These kernel-base sockets are signifantly faster than conventional UDP/IP sockets. It should be noted that tcd supports DVSI-3003 nad DVSI-3000 devices, which it uses for AMBE vocoding.
This build support *dual-stack* operation, so the server on which it's running, must have both an IPv4 and IPv6 routable address if you are going to configure a dual-stack reflector. URF can support out-going DExtra links, by adding a new DExtra Peer type *and* it has many changes designed to increase reliability and stability.

@ -16,6 +16,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/stat.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
@ -34,10 +35,8 @@
#define JBLACKLISTPATH "BlacklistPath"
#define JBRANDMEISTER "Brandmeister"
#define JCALLSIGN "Callsign"
#define JCLIENTSPATH "ClientFilePath"
#define JCOUNTRY "Country"
#define JDCS "DCS"
#define JDEFAULTCALLSIGN "DefaultCallsign"
#define JDEFAULTID "DefaultId"
#define JDEFAULTRXFREQ "DefaultRxFreq"
#define JDEFAULTTXFREQ "DefaultTxFreq"
@ -46,8 +45,10 @@
#define JDMRIDDB "DMR ID DB"
#define JDMRPLUS "DMRPlus"
#define JDPLUS "DPlus"
#define JENABLE "Enable"
#define JFILES "Files"
#define JFILEPATH "FilePath"
#define JG3 "G3"
#define JG3TERMINALPATH "G3TerminalPath"
#define JINTERLINKPATH "InterlinkPath"
#define JIPADDRESSES "IpAddresses"
@ -58,6 +59,7 @@
#define JM17 "M17"
#define JMMDVM "MMDVM"
#define JMODE "Mode"
#define JMODULE "Module"
#define JMODULES "Modules"
#define JNAMES "Names"
#define JNXDNIDDB "NXDN ID DB"
@ -178,6 +180,8 @@ bool CConfigure::ReadData(const std::string &path)
section = ESection::dplus;
else if (0 == hname.compare(JDEXTRA))
section = ESection::dextra;
else if (0 == hname.compare(JG3))
section = ESection::g3;
else if (0 == hname.compare(JDMRPLUS))
section = ESection::dmrplus;
else if (0 == hname.compare(JMMDVM))
@ -241,7 +245,7 @@ bool CConfigure::ReadData(const std::string &path)
{
case ESection::names:
if (0 == key.compare(JCALLSIGN))
data[g_Keys.names.cs] = value;
data[g_Keys.names.callsign] = value;
else if (0 == key.compare(JSYSOPEMAIL))
data[g_Keys.names.email] = value;
else if (0 == key.compare(JCOUNTRY))
@ -313,6 +317,8 @@ bool CConfigure::ReadData(const std::string &path)
case ESection::bm:
if (0 == key.compare(JPORT))
data[g_Keys.bm.port] = getUnsigned(value, "Brandmeister Port", 1024, 65535, 10002);
else if (0 == key.compare(JENABLE))
data[g_Keys.bm.enable] = IS_TRUE(value[0]);
else
badParam(key);
break;
@ -328,6 +334,12 @@ bool CConfigure::ReadData(const std::string &path)
else
badParam(key);
break;
case ESection::g3:
if (0 == key.compare(JENABLE))
data[g_Keys.g3.enable] = IS_TRUE(value[0]);
else
badParam(key);
break;
case ESection::dmrplus:
if (0 == key.compare(JPORT))
data[g_Keys.dmrplus.port] = getUnsigned(value, "DMRPlus Port", 1024, 65535, 8880);
@ -381,21 +393,16 @@ bool CConfigure::ReadData(const std::string &path)
badParam(key);
break;
case ESection::usrp:
if (0 == key.compare(JPORT))
data[g_Keys.usrp.port] = getUnsigned(value, "USRP Port", 1024, 65535, 34001);
else if (0 == key.compare(JAUTOLINKMODULE))
setAutolink(JUSRP, g_Keys.usrp.autolinkmod, value);
else if (0 == key.compare(JDEFAULTCALLSIGN))
{
std::string cs;
for (auto &c : value)
if (isalnum(c))
cs.append(1, toupper(c));
if (cs.size() > 7) cs.resize(7);
data[g_Keys.usrp.defaultcallsign] = cs;
}
else if (0 == key.compare(JCLIENTSPATH))
data[g_Keys.usrp.clientfilepath] = value;
if (0 == key.compare(JENABLE))
data[g_Keys.usrp.enable] = IS_TRUE(value[0]);
else if (0 == key.compare(JPORT))
data[g_Keys.usrp.port] = getUnsigned(value, "USRP Port", 1024, 65535, 32000);
else if (0 == key.compare(JMODULE))
data[g_Keys.usrp.module] = value.substr(0, 1);
else if (0 == key.compare(JCALLSIGN))
data[g_Keys.usrp.callsign] = value;
else if (0 == key.compare(JFILEPATH))
data[g_Keys.usrp.filepath] = value;
else
badParam(key);
break;
@ -478,13 +485,13 @@ bool CConfigure::ReadData(const std::string &path)
////////////////////////////// check the input
// Names section
if (isDefined(ErrorLevel::fatal, JNAMES, JCALLSIGN, g_Keys.names.cs, rval))
if (isDefined(ErrorLevel::fatal, JNAMES, JCALLSIGN, g_Keys.names.callsign, rval))
{
const auto cs = data[g_Keys.names.cs].get<std::string>();
const auto cs = data[g_Keys.names.callsign].get<std::string>();
auto RefRegEx = std::regex("^URF([A-Z0-9]){3,3}$", std::regex::extended);
if (! std::regex_match(cs, RefRegEx))
{
std::cerr << "ERROR: [" << JNAMES << "] " << JCALLSIGN << " '" << cs << "' is malformed" << std::endl;
std::cerr << "ERROR: [" << JNAMES << ']' << JCALLSIGN << " '" << cs << "' is malformed" << std::endl;
rval = true;
}
}
@ -586,7 +593,7 @@ bool CConfigure::ReadData(const std::string &path)
// how many transcoded modules
auto size = tcmods.size();
if (3 != size && 1 != size)
std::cout << "WARNING: [" << JMODULES << "] " << JTRANSCODED << " doesn't define three (or one) modules" << std::endl;
std::cout << "WARNING: [" << JMODULES << ']' << JTRANSCODED << " doesn't define three (or one) modules" << std::endl;
// make sure each transcoded module is configured
for (auto c : data[g_Keys.modules.tcmodules])
@ -626,29 +633,68 @@ bool CConfigure::ReadData(const std::string &path)
}
// "simple" protocols with only a Port
isDefined(ErrorLevel::fatal, JBRANDMEISTER, JPORT, g_Keys.bm.port, rval);
isDefined(ErrorLevel::fatal, JDCS, JPORT, g_Keys.dcs.port, rval);
isDefined(ErrorLevel::fatal, JDEXTRA, JPORT, g_Keys.dextra.port, rval);
isDefined(ErrorLevel::fatal, JDMRPLUS, JPORT, g_Keys.dmrplus.port, rval);
isDefined(ErrorLevel::fatal, JDPLUS, JPORT, g_Keys.dplus.port, rval);
isDefined(ErrorLevel::fatal, JM17, JPORT, g_Keys.m17.port, rval);
isDefined(ErrorLevel::fatal, JURF, JPORT, g_Keys.urf.port, rval);
// BM
if (isDefined(ErrorLevel::fatal, JBRANDMEISTER, JENABLE, g_Keys.bm.enable, rval))
{
if (GetBoolean(g_Keys.bm.enable))
{
isDefined(ErrorLevel::fatal, JBRANDMEISTER, JPORT, g_Keys.bm.port, rval);
}
}
// G3
isDefined(ErrorLevel::fatal, JG3, JENABLE, g_Keys.g3.enable, rval);
// MMDVM
isDefined(ErrorLevel::fatal, JMMDVM, JPORT, g_Keys.mmdvm.port, rval);
isDefined(ErrorLevel::fatal, JMMDVM, JDEFAULTID, g_Keys.mmdvm.defaultid, rval);
// NXDN
isDefined(ErrorLevel::fatal, JNXDN, JPORT, g_Keys.nxdn.port, rval);
checkAutoLink(JNXDN, JAUTOLINKMODULE, g_Keys.nxdn.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JNXDN, JREFLECTORID, g_Keys.nxdn.reflectorid, rval);
// P25
isDefined(ErrorLevel::fatal, JP25, JPORT, g_Keys.p25.port, rval);
checkAutoLink(JP25, JAUTOLINKMODULE, g_Keys.p25.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JP25, JREFLECTORID, g_Keys.p25.reflectorid, rval);
// USRP
isDefined(ErrorLevel::fatal, JUSRP, JPORT, g_Keys.usrp.port, rval);
checkAutoLink(JUSRP, JAUTOLINKMODULE, g_Keys.usrp.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JUSRP, JDEFAULTCALLSIGN, g_Keys.usrp.defaultcallsign, rval);
isDefined(ErrorLevel::fatal, JUSRP, JCLIENTSPATH, g_Keys.usrp.clientfilepath, rval);
if (isDefined(ErrorLevel::fatal, JUSRP, JENABLE, g_Keys.usrp.enable, rval))
{
if (GetBoolean(g_Keys.usrp.enable))
{
if (IsString(g_Keys.modules.tcmodules))
{
if (isDefined(ErrorLevel::fatal, JUSRP, JMODULE, g_Keys.usrp.module, rval))
{
if (std::string::npos == GetString(g_Keys.modules.tcmodules).find(GetString(g_Keys.usrp.module).at(0)))
{
std::cerr << "ERROR: [" << JUSRP << ']' << JMODULE << " is not a transcoded module" << std::endl;
rval = true;
}
}
isDefined(ErrorLevel::fatal, JUSRP, JPORT, g_Keys.usrp.port, rval);
isDefined(ErrorLevel::fatal, JUSRP, JCALLSIGN, g_Keys.usrp.callsign, rval);
//if (isDefined(ErrorLevel::fatal, JUSRP, JFILEPATH, g_Keys.usrp.filepath, rval))
if (data.contains(g_Keys.usrp.filepath))
checkFile(JUSRP, JFILEPATH, data[g_Keys.usrp.filepath]);
}
else
{
std::cerr << "ERROR: " << JUSRP << " requires a transoder" << std::endl;
rval = true;
}
}
}
// YSF
isDefined(ErrorLevel::fatal, JYSF, JPORT, g_Keys.ysf.port, rval);
checkAutoLink(JYSF, JAUTOLINKMODULE, g_Keys.ysf.autolinkmod, rval);
@ -657,6 +703,7 @@ bool CConfigure::ReadData(const std::string &path)
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONID, g_Keys.ysf.ysfreflectordb.id, rval);
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONNAME, g_Keys.ysf.ysfreflectordb.name, rval);
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONDESCRIPTION, g_Keys.ysf.ysfreflectordb.description, rval);
// Databases
std::list<std::pair<const std::string, const struct SJsonKeys::DB *>> dbs = {
{ JDMRIDDB, &g_Keys.dmriddb },
@ -665,18 +712,36 @@ bool CConfigure::ReadData(const std::string &path)
};
for ( auto &item : dbs )
{
isDefined(ErrorLevel::fatal, item.first, JURL, item.second->url, rval);
isDefined(ErrorLevel::fatal, item.first, JMODE, item.second->mode, rval);
isDefined(ErrorLevel::fatal, item.first, JREFRESHMIN, item.second->refreshmin, rval);
isDefined(ErrorLevel::fatal, item.first, JFILEPATH, item.second->filepath, rval);
if (isDefined(ErrorLevel::fatal, item.first, JMODE, item.second->mode, rval))
{
if (ERefreshType::file != GetRefreshType(item.second->mode))
{
isDefined(ErrorLevel::fatal, item.first, JURL, item.second->url, rval);
isDefined(ErrorLevel::fatal, item.first, JREFRESHMIN, item.second->refreshmin, rval);
}
if (ERefreshType::http != GetRefreshType(item.second->mode))
{
if (isDefined(ErrorLevel::fatal, item.first, JFILEPATH, item.second->filepath, rval))
checkFile(item.first, JFILEPATH, data[item.second->filepath]);
}
}
}
// Other files
isDefined(ErrorLevel::fatal, JFILES, JPIDPATH, g_Keys.files.pid, rval);
isDefined(ErrorLevel::fatal, JFILES, JREFLSTATEPATH, g_Keys.files.state, rval);
isDefined(ErrorLevel::fatal, JFILES, JWHITELISTPATH, g_Keys.files.white, rval);
isDefined(ErrorLevel::fatal, JFILES, JBLACKLISTPATH, g_Keys.files.black, rval);
isDefined(ErrorLevel::fatal, JFILES, JINTERLINKPATH, g_Keys.files.interlink, rval);
isDefined(ErrorLevel::fatal, JFILES, JG3TERMINALPATH, g_Keys.files.terminal, rval);
if (isDefined(ErrorLevel::fatal, JFILES, JWHITELISTPATH, g_Keys.files.white, rval))
checkFile(JFILES, JWHITELISTPATH, data[g_Keys.files.white]);
if (isDefined(ErrorLevel::fatal, JFILES, JBLACKLISTPATH, g_Keys.files.black, rval))
checkFile(JFILES, JBLACKLISTPATH, data[g_Keys.files.black]);
if (isDefined(ErrorLevel::fatal, JFILES, JINTERLINKPATH, g_Keys.files.interlink, rval))
checkFile(JFILES, JINTERLINKPATH, data[g_Keys.files.interlink]);
if (data.contains(g_Keys.g3.enable) && GetBoolean(g_Keys.g3.enable))
{
if (isDefined(ErrorLevel::fatal, JFILES, JG3TERMINALPATH, g_Keys.files.terminal, rval))
checkFile(JFILES, JG3TERMINALPATH, data[g_Keys.files.terminal]);
}
return rval;
}
@ -688,12 +753,12 @@ bool CConfigure::isDefined(ErrorLevel level, const std::string &section, const s
if (ErrorLevel::mild == level)
{
std::cout << "WARNING: [" << section << "] " << pname << " is not defined" << std::endl;
std::cout << "WARNING: [" << section << ']' << pname << " is not defined" << std::endl;
data[key] = nullptr;
}
else
{
std::cerr << "ERROR: [" << section << "] " << pname << " is not defined" << std::endl;
std::cerr << "ERROR: [" << section << ']' << pname << " is not defined" << std::endl;
rval = true;
}
return false;
@ -708,7 +773,7 @@ void CConfigure::checkAutoLink(const std::string &section, const std::string &pn
const auto c = data[key].get<std::string>().at(0);
if (std::string::npos == mods.find(c))
{
std::cerr << "ERROR: [" << section << "] " << pname << " module '" << c << "' not a configured module" << std::endl;
std::cerr << "ERROR: [" << section << ']' << pname << " module '" << c << "' not a configured module" << std::endl;
rval = true;
}
}
@ -767,6 +832,16 @@ void CConfigure::setAutolink(const std::string &section, const std::string &key,
std::cout << "WARNING: line #" << counter << ": " << section << " AutoLinkModule is invalid: '" << value.substr(0, 1) << "'" << std::endl;
}
void CConfigure::checkFile(const std::string &section, const std::string &key, const std::string &filepath) const
{
struct stat sstat;
auto rval = stat(filepath.c_str(), &sstat);
if (rval)
{
std::cout << "WARNING: [" << section << ']' << key << " \"" << filepath << "\": " << strerror(errno) << std::endl;
}
}
void CConfigure::Dump(bool justpublic) const
{
nlohmann::json tmpjson = data;
@ -843,6 +918,14 @@ unsigned CConfigure::GetUnsigned(const std::string &key) const
return u;
}
bool CConfigure::GetBoolean(const std::string &key) const
{
if (data[key].is_boolean())
return data[key];
else
return false;
}
char CConfigure::GetAutolinkModule(const std::string &key) const
{
char c = 0;

@ -25,7 +25,7 @@
enum class ErrorLevel { fatal, mild };
enum class ERefreshType { file, http, both };
enum class ESection { none, names, ip, modules, urf, dplus, dextra, dcs, dmrplus, mmdvm, nxdn, bm, ysf, p25, m17, usrp, dmrid, nxdnid, ysffreq, files };
enum class ESection { none, names, ip, modules, urf, dplus, dextra, dcs, g3, dmrplus, mmdvm, nxdn, bm, ysf, p25, m17, usrp, dmrid, nxdnid, ysffreq, files };
#define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1')
@ -37,6 +37,7 @@ public:
void Dump(bool justpublic) const;
std::string GetString(const std::string &key) const;
unsigned GetUnsigned(const std::string &key) const;
bool GetBoolean(const std::string &key) const;
ERefreshType GetRefreshType(const std::string &key) const;
bool IsString(const std::string &key) const;
char GetAutolinkModule(const std::string &key) const;
@ -51,6 +52,7 @@ private:
unsigned getUnsigned(const std::string &value, const std::string &label, unsigned min, unsigned max, unsigned defaultvalue) const;
void badParam(const std::string &param) const;
bool checkModules(std::string &m) const;
void checkFile(const std::string &section, const std::string &key, const std::string &filepath) const;
void setAutolink(const std::string &section, const std::string &key, const std::string &value);
bool isDefined(ErrorLevel level, const std::string &section, const std::string &pname, const std::string &key, bool &rval);
void checkAutoLink(const std::string &section, const std::string &pname, const std::string &key, bool &rval);

@ -23,35 +23,49 @@
// configuration key names
struct SJsonKeys {
struct PORTONLY { const std::string port; }
bm { "BrandMeisterPort" },
dcs { "DCSPort" },
dextra { "DExtraPort" },
dmrplus { "DMRPlusPort" },
dplus { "DPlusPort" },
m17 { "M17Port" },
urf { "URFPort" };
struct G3 { const std::string enable; }
g3 { "G3Enable" };
struct BM { const std::string enable, port; }
bm { "bmEnable", "bmPort" };
struct MMDVM { const std::string port, defaultid; }
mmdvm { "MMDVMPort", "mmdvmdefaultid" };
struct NAMES { const std::string cs, email, country, sponsor; }
struct NAMES { const std::string callsign, email, country, sponsor; }
names { "Callsign", "SysopEmail", "Country", "Sponsor" };
struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; }
ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" };
struct MODULES { const std::string modules, tcmodules, descriptor[26]; }
modules { "Modules", "TranscodedModules",
"DescriptionA", "DescriptionB", "DescriptionC", "DescriptionD", "DescriptionE", "DescriptionF", "DescriptionG", "DescriptionH", "DescriptionI", "DescriptionJ", "DescriptionK", "DescriptionL", "DescriptionM", "DescriptionN", "DescriptionO", "DescriptionP", "DescriptionQ", "DescriptionR", "DescriptionS", "DescriptionT", "DescriptionU", "DescriptionV", "DescriptionW", "DescriptionX", "DescriptionY", "DescriptionZ" };
struct USRP { const std::string port, autolinkmod, defaultcallsign, clientfilepath; }
usrp { "USRPPort", "USRPAutolinkMod", "usrpDefaultCallsign", "usrpClientfilePath" };
struct USRP { const std::string enable, port, module, callsign, filepath; }
usrp { "usrpEnable", "urspPort", "usrpModule", "usrpCallsign", "usrpFilePath" };
struct P25NXDN { const std::string port, autolinkmod, reflectorid; }
p25 { "P25Port", "P25AutolinkMod", "P25ReflectorID" },
nxdn { "NXDNPort", "NXDNAutolinkMod", "NXDNReflectorID" };
struct YSF { const std::string port, autolinkmod, defaulttxfreq, defaultrxfreq;
struct YSLREG { const std::string id, name, description; } ysfreflectordb; }
ysf { "YSFPort", "YSFAutoLinkMod", "YSFDefaultTxFreq", "YSFDefaultRxFreq",
{ "ysfrefdbid", "ysfrefdbname", "ysfrefdbdesc" } };
struct DB { const std::string url, mode, refreshmin, filepath; }
dmriddb { "dmrIdDbUrl", "dmrIdDbMode", "dmrIdDbRefresh", "dmrIdDbFilePath" },
nxdniddb { "nxdnIdDbUrl", "nxdnIdDbMode", "nxdnIdDbRefresh", "nxdnIdDbFilePath" },
ysftxrxdb { "ysfIdDbUrl", "ysfIdDbMode", "ysfIdDbRefresh", "ysfIdDbFilePath" };
struct FILES { const std::string pid, state, white, black, interlink, terminal; }
files { "pidFilePath", "stateFilePath", "whitelistFilePath", "blacklistFilePath", "interlinkFilePath", "g3TerminalFilePath" };
};

@ -49,7 +49,7 @@ int main(int argc, char *argv[])
std::cout << "IPv4 binding address is '" << g_Conf.GetString(g_Keys.ip.ipv4bind) << "'" << std::endl;
// remove pidfile
const std::string pidpath(g_Conf.GetString(g_Keys.files.pid));
const std::string callsign(g_Conf.GetString(g_Keys.names.cs));
const std::string callsign(g_Conf.GetString(g_Keys.names.callsign));
remove(pidpath.c_str());
// splash

@ -64,9 +64,12 @@ bool CProtocols::Init(void)
if (! m_Protocols.back()->Initialize(nullptr, EProtocol::dmrmmdvm, uint16_t(g_Conf.GetUnsigned(g_Keys.mmdvm.port)), DMR_IPV4, DMR_IPV6))
return false;
m_Protocols.emplace_back(std::unique_ptr<CBMProtocol>(new CBMProtocol));
if (! m_Protocols.back()->Initialize("XLX", EProtocol::bm, uint16_t(g_Conf.GetUnsigned(g_Keys.bm.port)), DMR_IPV4, DMR_IPV6))
return false;
if (g_Conf.GetBoolean(g_Keys.bm.enable))
{
m_Protocols.emplace_back(std::unique_ptr<CBMProtocol>(new CBMProtocol));
if (! m_Protocols.back()->Initialize("XLX", EProtocol::bm, uint16_t(g_Conf.GetUnsigned(g_Keys.bm.port)), DMR_IPV4, DMR_IPV6))
return false;
}
m_Protocols.emplace_back(std::unique_ptr<CDmrplusProtocol>(new CDmrplusProtocol));
if (! m_Protocols.back()->Initialize(nullptr, EProtocol::dmrplus, uint16_t(g_Conf.GetUnsigned(g_Keys.dmrplus.port)), DMR_IPV4, DMR_IPV6))
@ -88,17 +91,23 @@ bool CProtocols::Init(void)
if (! m_Protocols.back()->Initialize("NXDN", EProtocol::nxdn, uint16_t(g_Conf.GetUnsigned(g_Keys.nxdn.port)), NXDN_IPV4, NXDN_IPV6))
return false;
m_Protocols.emplace_back(std::unique_ptr<CUSRPProtocol>(new CUSRPProtocol));
if (! m_Protocols.back()->Initialize("USRP", EProtocol::usrp, uint16_t(g_Conf.GetUnsigned(g_Keys.usrp.port)), USRP_IPV4, USRP_IPV6))
return false;
if (g_Conf.GetBoolean(g_Keys.usrp.enable))
{
m_Protocols.emplace_back(std::unique_ptr<CUSRPProtocol>(new CUSRPProtocol));
if (! m_Protocols.back()->Initialize("USRP", EProtocol::usrp, uint16_t(g_Conf.GetUnsigned(g_Keys.usrp.port)), USRP_IPV4, USRP_IPV6))
return false;
}
m_Protocols.emplace_back(std::unique_ptr<CURFProtocol>(new CURFProtocol));
if (! m_Protocols.back()->Initialize("URF", EProtocol::urf, uint16_t(g_Conf.GetUnsigned(g_Keys.urf.port)), URF_IPV4, URF_IPV6))
return false;
m_Protocols.emplace_back(std::unique_ptr<CG3Protocol>(new CG3Protocol));
if (! m_Protocols.back()->Initialize("XLX", EProtocol::g3, G3_DV_PORT, DMR_IPV4, DMR_IPV6))
if (g_Conf.GetBoolean(g_Keys.g3.enable))
{
m_Protocols.emplace_back(std::unique_ptr<CG3Protocol>(new CG3Protocol));
if (! m_Protocols.back()->Initialize("XLX", EProtocol::g3, G3_DV_PORT, DMR_IPV4, DMR_IPV6))
return false;
}
}
m_Mutex.unlock();

@ -47,7 +47,7 @@ CReflector::~CReflector()
bool CReflector::Start(void)
{
// get config stuff
m_Callsign = CCallsign(g_Conf.GetString(g_Keys.names.cs).c_str(), false);
m_Callsign = CCallsign(g_Conf.GetString(g_Keys.names.callsign).c_str(), false);
m_Modules.assign(g_Conf.GetString(g_Keys.modules.modules));
std::string tcmods(g_Conf.GetString(g_Keys.modules.tcmodules));
@ -273,15 +273,15 @@ void CReflector::RouterThread(const char ThisModule)
m_Protocols.Lock();
for ( auto it=m_Protocols.begin(); it!=m_Protocols.end(); it++ )
{
// make a copy! after the Push(tmp), tmp will be nullptr!
auto copy = packet->Copy();
// if packet is header, update RPT2 according to protocol
if ( copy->IsDvHeader() )
{
// get our callsign
// make the protocol-patched reflector callsign
CCallsign csRPT = (*it)->GetReflectorCallsign();
csRPT.SetCSModule(ThisModule);
// and put it in the copy
(dynamic_cast<CDvHeaderPacket *>(copy.get()))->SetRpt2Callsign(csRPT);
}

@ -1,8 +1,5 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
// Copyright © 2023 Doug McLain AD8DP
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -37,13 +34,8 @@ const uint8_t TLV_TAG_SET_INFO = 8;
bool CUSRPProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{
// config data
m_DefaultCallsign.assign(g_Conf.GetString(g_Keys.usrp.defaultcallsign));
m_AutolinkModule = g_Conf.GetAutolinkModule(g_Keys.usrp.autolinkmod);
CBuffer buffer;
m_uiStreamId = 0;
CCallsign cs(m_DefaultCallsign.c_str());
CClients *clients = g_Refl.GetClients();
std::ifstream file;
std::streampos size;
@ -51,7 +43,10 @@ bool CUSRPProtocol::Initialize(const char *type, const EProtocol ptype, const ui
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false;
file.open(g_Conf.GetString(g_Keys.usrp.clientfilepath), std::ios::in | std::ios::binary | std::ios::ate);
m_CStr.assign(g_Conf.GetString(g_Keys.usrp.callsign));
m_Module = g_Conf.GetString(g_Keys.usrp.module).at(0);
file.open(g_Conf.GetString(g_Keys.usrp.filepath), std::ios::in | std::ios::binary | std::ios::ate);
if ( file.is_open() )
{
// read file
@ -78,24 +73,24 @@ bool CUSRPProtocol::Initialize(const char *type, const EProtocol ptype, const ui
*ptr2 = 0;
char *ip;
char *port;
if ((ip = ::strtok(ptr1, ";")) != nullptr)
{
if ( ((port = ::strtok(nullptr, ";")) != nullptr) )
{
uint32_t ui = atoi(port);
CIp Ip(AF_INET, ui, ip);
auto newclient = std::make_shared<CUSRPClient>(cs, Ip);
if (m_AutolinkModule)
newclient->SetReflectorModule(m_AutolinkModule);
char *clientcs;
clients->AddClient(newclient);
}
if ( ((ip = ::strtok(ptr1, ";")) != nullptr) &&
((port = ::strtok(nullptr, ";")) != nullptr) &&
((clientcs = ::strtok(nullptr, ";")) != nullptr) )
{
uint32_t ui = atoi(port);
CIp Ip(AF_INET, ui, ip);
CCallsign cs(clientcs);
auto newclient = std::make_shared<CUSRPClient>(cs, Ip);
newclient->SetReflectorModule(m_Module);
g_Refl.GetClients()->AddClient(newclient);
g_Refl.ReleaseClients();
}
ptr1 = ptr2+1;
}
}
g_Refl.ReleaseClients();
// update time
m_LastKeepaliveTime.start();
@ -279,12 +274,14 @@ bool CUSRPProtocol::IsValidDvPacket(const CIp &Ip, const CBuffer &Buffer, std::u
if ( !stream )
{
m_uiStreamId = static_cast<uint32_t>(::rand());
CCallsign csMY = CCallsign(m_DefaultCallsign.c_str());
CCallsign rpt1 = CCallsign(m_DefaultCallsign.c_str());
CCallsign rpt2 = m_ReflectorCallsign;
rpt1.SetCSModule(USRP_MODULE_ID);
CCallsign csMY, rpt1, rpt2, csUR;
csMY.SetCallsign(m_CStr, false);
rpt1.SetCallsign(m_CStr, false);
rpt2 = m_ReflectorCallsign;
csUR.SetCallsign("CQCQCQ", false);
rpt1.SetCSModule(m_Module);
rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, true));
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, csUR, rpt1, rpt2, m_uiStreamId, true));
OnDvHeaderPacketIn(header, Ip);
}
@ -310,9 +307,11 @@ bool CUSRPProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer,
CCallsign csMY = CCallsign("", uiSrcId);
CCallsign rpt1 = CCallsign("", uiSrcId);
CCallsign rpt2 = m_ReflectorCallsign;
rpt1.SetCSModule(USRP_MODULE_ID);
CCallsign csUR;
csUR.SetCallsign("CQCQCQ", false);
rpt1.SetCSModule(m_Module);
rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, true));
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, csUR, rpt1, rpt2, m_uiStreamId, true));
}
return true;
}

@ -1,8 +1,5 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
// Copyright © 2023 Doug McLain AD8DP
//
// 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,12 +21,9 @@
#include "Protocol.h"
#include "DVHeaderPacket.h"
#include "DVFramePacket.h"
#include "Timer.h"
#include "Clients.h"
////////////////////////////////////////////////////////////////////////////////////////
// define
#define USRP_MODULE_ID 'B'
////////////////////////////////////////////////////////////////////////////////////////
// class
class CUSRPStreamCacheItem
@ -74,7 +68,8 @@ protected:
std::unordered_map<char, CUSRPStreamCacheItem> m_StreamsCache;
uint32_t m_uiStreamId;
// config data
std::string m_DefaultCallsign;
char m_AutolinkModule;
private:
// CConfigure data
std::string m_CStr;
char m_Module;
};

@ -21,6 +21,7 @@ IPv4Binding = 0.0.0.0
# define if you want to override what urfd finds using ipv4.icanhazip.com
# IPv4External = 4.3.2.1
# define if you want to override what urfd finds using ipv6.icanhazip.com
# IPv6External = f:e:d:c:b:a:9:0
@ -39,6 +40,7 @@ DescriptionZ = Temp Meeting
# Protocols
[Brandmeister]
Enable = false # Set to true if you've configured BM connections in your urfd.interlink file.
Port = 10002
[DCS]
@ -47,12 +49,15 @@ Port = 30051
[DExtra]
Port = 30001
[DMRPlus]
Port = 8880
[DPlus]
Port = 20001
[G3]
Enable = true
[DMRPlus]
Port = 8880
[M17]
Port = 17000
@ -74,10 +79,11 @@ ReflectorID = 12345
Port = 10017
[USRP]
Port = 34001
AutoLinkModule = A # comment out if you want to disable AL
DefaultCallsign = ALLSTAR
ClientFilePath = /usr/local/etc/USRPClients.txt
Enable = false
Port = 32000
Module = A # this has to be a transcoded module!
Callsign = ALLSTAR
#FilePath = /home/usr/Clients.txt # An experimental area of development
[YSF]
Port = 42000
@ -93,20 +99,20 @@ RegistrationDescription = URF Reflector
[DMR ID DB]
Mode = http #### Mode is "http", "file", or "both"
#### if "both", the url will be read first
FilePath = /usr/local/etc/dmrid.dat # for you to add your own values
# will be reloaded quickly, about 10s
FilePath = /home/user/dmrid.dat # for you to add your own values
# will be reloaded within 10s
URL = http://xlxapi.rlx.lu/api/exportdmr.php # if Mode "http" or "both"
RefreshMin = 179 # RefreshMin will only be used if the Mode is "file"
RefreshMin = 179
[NXDN ID DB]
Mode = http
FilePath = /usr/local/etc/nxdn.dat
FilePath = /home/user/nxdn.dat
URL = https://radioid.net/static/nxdn.csv
RefreshMin = 1440 # radioid.net says this file is updated once/day
[YSF TX/RX DB]
Mode = http
FilePath = /usr/local/etc/ysfnode.dat
FilePath = /home/user/ysfnode.dat
URL = http://xlxapi.rlx.lu/api/exportysfrepeaters.php
RefreshMin = 191
@ -114,7 +120,7 @@ RefreshMin = 191
[Files]
PidPath = /var/run/urfd.pid
ReflStatePath = /var/log/urfd.xml
WhitelistPath = /usr/local/etc/urfd.whitelist
BlacklistPath = /usr/local/etc/urfd.blacklist
InterlinkPath = /usr/local/etc/urfd.interlink
G3TerminalPath = /usr/local/etc/urfd.terminal
WhitelistPath = /home/user/urfd.whitelist
BlacklistPath = /home/user/urfd.blacklist
InterlinkPath = /home/user/urfd.interlink
G3TerminalPath = /home/user/urfd.terminal

Loading…
Cancel
Save

Powered by TurnKey Linux.