Compare commits
260 Commits
ef2889af23
...
ca76dfc728
Author | SHA1 | Date |
---|---|---|
Jonathan Naylor | ca76dfc728 | |
k7pws | a8644497bb | |
Jonathan Naylor | 7b458b8afb | |
Jonathan Naylor | 80a32f358f | |
Jonathan Naylor | 4cd7e99283 | |
Jonathan Naylor | cf804904f7 | |
W4END | e010f4e2fd | |
Jonathan Naylor | 54dcfded80 | |
Jonathan Naylor | 2df088c3d5 | |
Michele | c60a8618dc | |
W4END | 6a8f84c666 | |
Jonathan Naylor | ccc4af8b75 | |
Jonathan Naylor | 3b0d2c4a3a | |
maq1017 | 370c7c4465 | |
Steve Miller | 09b7405c0e | |
Jonathan Naylor | a11d2b0dbb | |
dl9cma | b8ec1b079a | |
Jonathan Naylor | 6b4ac07a98 | |
Jonathan Naylor | 7df53ae813 | |
Jorge Jesus Sainz Casalla | 94e100f3b6 | |
UR5WLA | e1e181da91 | |
Jonathan Naylor | 8e8bad4fff | |
Jonathan Naylor | a6b1c78a2d | |
PU5SMS | a8ee46e7f0 | |
Jonathan Naylor | a805717730 | |
PU5SMS | 451e51b4a4 | |
Merlinv3 | 55d18fa10b | |
Jonathan Naylor | 05b9990846 | |
Matt Fiscus | 65ff0c740c | |
Matt Fiscus | e8d5d91680 | |
Jonathan Naylor | 8875e20570 | |
mfiscus | 35a04a4472 | |
Jonathan Naylor | 1f140d8e28 | |
Heonmin Lee | 0746ded83b | |
Jonathan Naylor | d504e84738 | |
M0JGX | 0382d31ecc | |
Jonathan Naylor | de3d1086c3 | |
Jonathan Naylor | 843f12e2ca | |
Jonathan Naylor | 29e6bee0ec | |
Rudy | 5fdb97dc8d | |
Jonathan Naylor | 9c590e0009 | |
Gareth GM0WUR | 4a2a68b494 | |
Jonathan Naylor | 501fce4113 | |
ke8ane | 6544ed151b | |
Jonathan Naylor | 674d9e2424 | |
mfiscus | e7bdf23822 | |
Jonathan Naylor | e7316823af | |
Jonathan Naylor | 1a7804ed7f | |
Jonathan Naylor | 78ec1b00f9 | |
W2GLD | c23f238255 | |
Michele | 930fc9a03d | |
Jonathan Naylor | 15730cd271 | |
M0JGX | e553489ae5 | |
Jonathan Naylor | 7abe7706bb | |
Jonathan Naylor | cfcaab90ba | |
mfiscus | bc08575976 | |
Jonathan Naylor | 2a83f19a1a | |
Jonathan Naylor | 83965095d5 | |
Dave Behnke | 1138d26c87 | |
Dave Behnke | 2db8bcb31a | |
Jonathan Naylor | 6b021c2f46 | |
k7pws | 6d9764e43e | |
Jonathan Naylor | f798f99036 | |
kd8grn | 7f01d3d6b2 | |
Jonathan Naylor | 8d1dc5e145 | |
Fernando Recci | 9f1c1ac884 | |
Jonathan Naylor | bef256b258 | |
Ethan Gollehon | 2da36dad46 | |
Jonathan Naylor | 78eb626394 | |
bh1nyr | 4916ef66d5 | |
Jonathan Naylor | e20e3a3387 | |
M0JGX | 8da5e81596 | |
Jonathan Naylor | dd193c5396 | |
M0GLJ | e006e492f2 | |
Jonathan Naylor | c1d02c3396 | |
Jonathan Naylor | 964ad926b9 | |
Tony Langdon | 6ea9689870 | |
Jonathan Naylor | 46000902b2 | |
Jonathan Naylor | 52ff3db788 | |
Daniel Caujolle-Bert | f4914fe13c | |
Jonathan Naylor | 90dec58610 | |
Chipster | f71ce0ecf5 | |
Chipster | 1e53f55abb | |
Jonathan Naylor | 4ac963b1d0 | |
Jonathan Naylor | 668b32add6 | |
Kimberly McBlaze | 586d82df54 | |
Kimberly McBlaze | 73583c5765 | |
Jonathan Naylor | 4c37482329 | |
pu4ron | ac0b340808 | |
Jonathan Naylor | ca1428a6ce | |
Jonathan Naylor | e4ebc6ea23 | |
M0GLJ | 8b56973f32 | |
Jonathan Naylor | 80eeac53c1 | |
M0GLJ | 224acb9bfd | |
Jonathan Naylor | 0555e16a4c | |
pu4ron | 0423d53bf2 | |
Jonathan Naylor | 37a01bc52c | |
M0GLJ | c28169bd48 | |
Jonathan Naylor | 32144123b1 | |
Jonathan Naylor | e9371e6510 | |
Jonathan Naylor | 7ea328a4f2 | |
M0GLJ | 1b31929069 | |
M0GLJ | 7a8e4eef9b | |
Jonathan Naylor | f804894b88 | |
Merlinv3 | c322d555da | |
Jonathan Naylor | 486106a9c7 | |
Jonathan Naylor | 3bc2a1415b | |
M0VUB | 25399fbf90 | |
Jonathan Naylor | a7fe16eedd | |
N2WNS | c32a8ca641 | |
Jonathan Naylor | 1a9244ff44 | |
Chipster | 1474e16bb1 | |
Jonathan Naylor | 0e04fb82bd | |
Jonathan Naylor | 6d167eef2d | |
Merlinv3 | bc51b70195 | |
Merlinv3 | 3241ccad96 | |
Jonathan Naylor | d0c0ce16f5 | |
Jonathan Naylor | 2a455a83e1 | |
Chipster | e215ad6a1b | |
Chipster | a99083c25c | |
Jonathan Naylor | c9915170a0 | |
Chipster | 9f09acbd96 | |
Jonathan Naylor | 4b6e3afed2 | |
FredB1959 | 4cf46733a0 | |
Jonathan Naylor | 7eddd04566 | |
FredB1959 | 00e9ea8c9c | |
Jonathan Naylor | 1b7017acdc | |
Merlinv3 | 15123e1c25 | |
Jonathan Naylor | 291a5f9e12 | |
Jonathan Naylor | 151e82f9b8 | |
Rudy | aee966eb9c | |
Jonathan Naylor | e09f6ae4f8 | |
Rudy | c6b366d05d | |
Jonathan Naylor | 788c31c661 | |
Merlinv3 | 85cfa1631e | |
Merlinv3 | ff3084ff3c | |
Jonathan Naylor | 4f4637d854 | |
BudT | 1c0fbaf50e | |
Jonathan Naylor | e415982ed1 | |
Bj Lee | 1c3246e10a | |
Jonathan Naylor | 2486103fb8 | |
W2GLD | f3f056f6c0 | |
Jonathan Naylor | adf15fdc9f | |
Jonathan Naylor | d623451f71 | |
Hubok | eeeae8dcc7 | |
N2WNS | a4964b0e11 | |
N2WNS | 430c60f496 | |
Jonathan Naylor | 67f91534a2 | |
Jonathan Naylor | fa6e26b7be | |
Kool475 | df91158137 | |
Jonathan Naylor | 0c163b3437 | |
Jonathan Naylor | ddc9df34a5 | |
Bruno | 2597317152 | |
Jonathan Naylor | 1d2fa6a6dd | |
M0UPM | 587852f727 | |
Jonathan Naylor | 581a134076 | |
Christian OA4DOA | 393032e35e | |
Jonathan Naylor | 522448917e | |
Jonathan Naylor | 5b130de88f | |
pa3esz | 2821ee4aee | |
maq1017 | d7c8557448 | |
Jonathan Naylor | 7c1ab8ce7a | |
maq1017 | 45eb6ccde4 | |
Jonathan Naylor | b1e63d84e5 | |
Jonathan Naylor | b555fa101e | |
M0GLJ | 33706547dd | |
M0GLJ | 31941d9ba1 | |
Jonathan Naylor | 50abe6f672 | |
pa3frh | 5e03f005e4 | |
Jonathan Naylor | a18191b360 | |
Jonathan Naylor | ff0e1e7317 | |
Gareth GM0WUR | 1ff8a141ca | |
Jonathan Naylor | a2c73f7a3c | |
Jonathan Naylor | c830390836 | |
MW0MWZ | cc9609d4e2 | |
Jonathan Naylor | b559cf5eab | |
Merlinv3 | 208ff94c5a | |
Jonathan Naylor | 434c71d665 | |
Merlinv3 | 4f7ba10b45 | |
Jonathan Naylor | 38e6db94ad | |
Merlinv3 | 66fd14697e | |
Jonathan Naylor | 01e04579f7 | |
Esteban Mackay Q | f4f2cdb6ab | |
Jonathan Naylor | 77b930bf12 | |
Merlinv3 | 7868c805f2 | |
Jonathan Naylor | ad94417ef6 | |
Merlinv3 | b91cfade36 | |
Jonathan Naylor | df43986b50 | |
Merlinv3 | 7983a4c9b4 | |
Jonathan Naylor | 883e1006a7 | |
Guanchindeverin | ff57626365 | |
Guanchindeverin | ce6da5c7b5 | |
Guanchindeverin | 365fae1b9d | |
Jonathan Naylor | b24e860c6c | |
Merlinv3 | d510fe5ab4 | |
Jonathan Naylor | 52d151a9fd | |
Jonathan Naylor | a32ff79282 | |
Merlinv3 | 420a68811e | |
Jonathan Naylor | 02e693c00c | |
W2GLD | b367a5d22a | |
Jonathan Naylor | 44da73826b | |
jriner63 | 377a477019 | |
Jonathan Naylor | 206f9693f9 | |
Jonathan Naylor | 3f0141ecf2 | |
bh1nyr | 38e8129fe4 | |
bh1nyr | 0abd218697 | |
Jonathan Naylor | 923570b6d3 | |
Hubok | 1e3e43b30b | |
Jonathan Naylor | cccfaa9d9b | |
ku-mi | 3179316e66 | |
Jonathan Naylor | 19de95e3bf | |
bh1nyr | 0fee8b76ee | |
bh1nyr | 6f5903e63d | |
bh1nyr | 4963c24471 | |
Jonathan Naylor | 7ecdc46b3e | |
bh1nyr | 324c2d5a4d | |
Jonathan Naylor | 95f76bdb15 | |
Jonathan Naylor | fcdfc09404 | |
wb6dtb | f28214a070 | |
Jonathan Naylor | 00bba1d8b8 | |
bh1nyr | 3dfa9af218 | |
Jonathan Naylor | 6fc5c39297 | |
Jonathan Naylor | c2ff02da90 | |
Jonathan Naylor | 67e0ffe4fe | |
Jonathan Naylor | e9e40bd003 | |
Hubok | a750b37313 | |
Jonathan Naylor | b765c326f5 | |
Jonathan Naylor | 24b55d5283 | |
Kimberly McBlaze | 066b8b399c | |
Jonathan Naylor | 167f11c4b2 | |
W2GLD | def7427102 | |
Jonathan Naylor | e742038b8b | |
Jonathan Naylor | 01cb6fc3bb | |
Jonathan Naylor | 2fb0e606ab | |
Carlos (KP4CA) | 75d19607a9 | |
Jonathan Naylor | a5985b1176 | |
Jonathan Naylor | 5dddeefae8 | |
Jonathan Naylor | 9c4df30e58 | |
Kimberly McBlaze | c111b3c317 | |
Tony Langdon | 7fbaeccb44 | |
Stephan | bee0b78279 | |
Jonathan Naylor | 6668b3ef87 | |
Carlos (KP4CA) | 9602ae5d42 | |
Jonathan Naylor | eca0339b35 | |
Jonathan Naylor | 4ef2dc052b | |
Jonathan Naylor | 64538ec811 | |
Jonathan Naylor | e22ff6fe37 | |
firealarmss | 10ea9b704c | |
Jim Nessen | 312dda3e6b | |
Jonathan Naylor | 1124e4a1bb | |
Jonathan Naylor | 9e3256e8bd | |
Jonathan Naylor | 88bff78a73 | |
Inga Muste | a8f3b335f2 | |
Inga Muste | 44c777a2d1 | |
g0anv | dd1f81f612 | |
Jonathan Naylor | f9f1530dc9 | |
Daniel Caujolle-Bert | 4f3bc66544 | |
Daniel Caujolle-Bert | a69af8ad79 | |
Jonathan Naylor | f907b4ce3c | |
Daniel Caujolle-Bert | 3afa96c6cd |
|
@ -15,5 +15,6 @@ x64
|
||||||
.vs
|
.vs
|
||||||
NXDNGateway/NXDNGateway
|
NXDNGateway/NXDNGateway
|
||||||
NXDNParrot/NXDNParrot
|
NXDNParrot/NXDNParrot
|
||||||
NXDNReflector/NXDNReflector
|
NXDNGateway/GitVersion.h
|
||||||
|
NXDNParrot/GitVersion.h
|
||||||
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
SUBDIRS = NXDNGateway NXDNParrot NXDNReflector
|
SUBDIRS = NXDNGateway NXDNParrot
|
||||||
CLEANDIRS = $(SUBDIRS:%=clean-%)
|
CLEANDIRS = $(SUBDIRS:%=clean-%)
|
||||||
INSTALLDIRS = $(SUBDIRS:%=install-%)
|
INSTALLDIRS = $(SUBDIRS:%=install-%)
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.27130.2010
|
VisualStudioVersion = 16.0.31105.61
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNParrot", "NXDNParrot\NXDNParrot.vcxproj", "{2AE94EAA-FD57-45C9-8555-6425CFA777A3}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNParrot", "NXDNParrot\NXDNParrot.vcxproj", "{2AE94EAA-FD57-45C9-8555-6425CFA777A3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNGateway", "NXDNGateway\NXDNGateway.vcxproj", "{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNGateway", "NXDNGateway\NXDNGateway.vcxproj", "{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNReflector", "NXDNReflector\NXDNReflector.vcxproj", "{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
@ -33,14 +31,6 @@ Global
|
||||||
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x64.Build.0 = Release|x64
|
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x64.Build.0 = Release|x64
|
||||||
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.ActiveCfg = Release|Win32
|
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.Build.0 = Release|Win32
|
{8B7A5406-8560-4B40-ADDA-9B8EBF93E232}.Release|x86.Build.0 = Release|Win32
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.ActiveCfg = Debug|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.Build.0 = Debug|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.Build.0 = Release|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.ActiveCfg = Release|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -34,6 +34,7 @@ m_latitude(0.0F),
|
||||||
m_longitude(0.0F),
|
m_longitude(0.0F),
|
||||||
m_height(0),
|
m_height(0),
|
||||||
m_desc(),
|
m_desc(),
|
||||||
|
m_symbol(),
|
||||||
m_aprsAddr(),
|
m_aprsAddr(),
|
||||||
m_aprsAddrLen(0U),
|
m_aprsAddrLen(0U),
|
||||||
m_aprsSocket()
|
m_aprsSocket()
|
||||||
|
@ -61,11 +62,12 @@ CAPRSWriter::~CAPRSWriter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAPRSWriter::setInfo(unsigned int txFrequency, unsigned int rxFrequency, const std::string& desc)
|
void CAPRSWriter::setInfo(unsigned int txFrequency, unsigned int rxFrequency, const std::string& desc, const std::string& symbol)
|
||||||
{
|
{
|
||||||
m_txFrequency = txFrequency;
|
m_txFrequency = txFrequency;
|
||||||
m_rxFrequency = rxFrequency;
|
m_rxFrequency = rxFrequency;
|
||||||
m_desc = desc;
|
m_desc = desc;
|
||||||
|
m_symbol = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAPRSWriter::setStaticLocation(float latitude, float longitude, int height)
|
void CAPRSWriter::setStaticLocation(float latitude, float longitude, int height)
|
||||||
|
@ -173,19 +175,19 @@ void CAPRSWriter::sendIdFrameFixed()
|
||||||
char desc[200U];
|
char desc[200U];
|
||||||
if (m_txFrequency != 0U) {
|
if (m_txFrequency != 0U) {
|
||||||
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
|
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
|
||||||
::sprintf(desc, "MMDVM Voice %.5LfMHz %c%.4lfMHz%s%s",
|
::sprintf(desc, "MMDVM Voice (NXDN) %.5LfMHz %c%.4lfMHz%s%s",
|
||||||
(long double)(m_txFrequency) / 1000000.0F,
|
(long double)(m_txFrequency) / 1000000.0F,
|
||||||
offset < 0.0F ? '-' : '+',
|
offset < 0.0F ? '-' : '+',
|
||||||
::fabs(offset), m_desc.empty() ? "" : ", ", m_desc.c_str());
|
::fabs(offset), m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
} else {
|
} else {
|
||||||
::sprintf(desc, "MMDVM Voice%s%s", m_desc.empty() ? "" : ", ", m_desc.c_str());
|
::sprintf(desc, "MMDVM Voice (NXDN)%s%s", m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* band = "4m";
|
const char* band = "4m";
|
||||||
if (m_txFrequency >= 1200000000U)
|
if (m_txFrequency >= 1200000000U)
|
||||||
band = "1.2";
|
band = "23cm/1.2GHz";
|
||||||
else if (m_txFrequency >= 420000000U)
|
else if (m_txFrequency >= 420000000U)
|
||||||
band = "440";
|
band = "70cm";
|
||||||
else if (m_txFrequency >= 144000000U)
|
else if (m_txFrequency >= 144000000U)
|
||||||
band = "2m";
|
band = "2m";
|
||||||
else if (m_txFrequency >= 50000000U)
|
else if (m_txFrequency >= 50000000U)
|
||||||
|
@ -209,17 +211,21 @@ void CAPRSWriter::sendIdFrameFixed()
|
||||||
::sprintf(lon, "%08.2lf", longitude);
|
::sprintf(lon, "%08.2lf", longitude);
|
||||||
|
|
||||||
std::string server = m_callsign;
|
std::string server = m_callsign;
|
||||||
|
std::string symbol = m_symbol;
|
||||||
size_t pos = server.find_first_of('-');
|
size_t pos = server.find_first_of('-');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
server.append("-S");
|
server.append("-S");
|
||||||
else
|
else
|
||||||
server.append("S");
|
server.append("S");
|
||||||
|
|
||||||
|
if (symbol.empty())
|
||||||
|
symbol.append("D&");
|
||||||
|
|
||||||
char output[500U];
|
char output[500U];
|
||||||
::sprintf(output, "%s>APDG04,TCPIP*,qAC,%s:!%s%cD%s%c&/A=%06.0f%s %s\r\n",
|
::sprintf(output, "%s>APDG04,TCPIP*,qAC,%s:!%s%c%c%s%c%c/A=%06.0f%s %s\r\n",
|
||||||
m_callsign.c_str(), server.c_str(),
|
m_callsign.c_str(), server.c_str(),
|
||||||
lat, (m_latitude < 0.0F) ? 'S' : 'N',
|
lat, (m_latitude < 0.0F) ? 'S' : 'N', symbol[0],
|
||||||
lon, (m_longitude < 0.0F) ? 'W' : 'E',
|
lon, (m_longitude < 0.0F) ? 'W' : 'E', symbol[1],
|
||||||
float(m_height) * 3.28F, band, desc);
|
float(m_height) * 3.28F, band, desc);
|
||||||
|
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
|
@ -242,7 +248,11 @@ void CAPRSWriter::sendIdFrameMobile()
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if GPSD_API_MAJOR_VERSION >= 10
|
||||||
|
if (m_gpsdData.fix.status != STATUS_FIX)
|
||||||
|
#else
|
||||||
if (m_gpsdData.status != STATUS_FIX)
|
if (m_gpsdData.status != STATUS_FIX)
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool latlonSet = (m_gpsdData.set & LATLON_SET) == LATLON_SET;
|
bool latlonSet = (m_gpsdData.set & LATLON_SET) == LATLON_SET;
|
||||||
|
@ -266,19 +276,19 @@ void CAPRSWriter::sendIdFrameMobile()
|
||||||
char desc[200U];
|
char desc[200U];
|
||||||
if (m_txFrequency != 0U) {
|
if (m_txFrequency != 0U) {
|
||||||
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
|
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
|
||||||
::sprintf(desc, "MMDVM Voice %.5LfMHz %c%.4lfMHz%s%s",
|
::sprintf(desc, "MMDVM Voice (NXDN) %.5LfMHz %c%.4lfMHz%s%s",
|
||||||
(long double)(m_txFrequency) / 1000000.0F,
|
(long double)(m_txFrequency) / 1000000.0F,
|
||||||
offset < 0.0F ? '-' : '+',
|
offset < 0.0F ? '-' : '+',
|
||||||
::fabs(offset), m_desc.empty() ? "" : ", ", m_desc.c_str());
|
::fabs(offset), m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
} else {
|
} else {
|
||||||
::sprintf(desc, "MMDVM Voice%s%s", m_desc.empty() ? "" : ", ", m_desc.c_str());
|
::sprintf(desc, "MMDVM Voice (NXDN)%s%s", m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* band = "4m";
|
const char* band = "4m";
|
||||||
if (m_txFrequency >= 1200000000U)
|
if (m_txFrequency >= 1200000000U)
|
||||||
band = "1.2";
|
band = "23cm/1.2GHz";
|
||||||
else if (m_txFrequency >= 420000000U)
|
else if (m_txFrequency >= 420000000U)
|
||||||
band = "440";
|
band = "70cm";
|
||||||
else if (m_txFrequency >= 144000000U)
|
else if (m_txFrequency >= 144000000U)
|
||||||
band = "2m";
|
band = "2m";
|
||||||
else if (m_txFrequency >= 50000000U)
|
else if (m_txFrequency >= 50000000U)
|
||||||
|
@ -302,17 +312,21 @@ void CAPRSWriter::sendIdFrameMobile()
|
||||||
::sprintf(lon, "%08.2lf", longitude);
|
::sprintf(lon, "%08.2lf", longitude);
|
||||||
|
|
||||||
std::string server = m_callsign;
|
std::string server = m_callsign;
|
||||||
|
std::string symbol = m_symbol;
|
||||||
size_t pos = server.find_first_of('-');
|
size_t pos = server.find_first_of('-');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
server.append("-S");
|
server.append("-S");
|
||||||
else
|
else
|
||||||
server.append("S");
|
server.append("S");
|
||||||
|
|
||||||
|
if (symbol.empty())
|
||||||
|
symbol.append("D&");
|
||||||
|
|
||||||
char output[500U];
|
char output[500U];
|
||||||
::sprintf(output, "%s>APDG03,TCPIP*,qAC,%s:!%s%cD%s%c&",
|
::sprintf(output, "%s>APDG03,TCPIP*,qAC,%s:!%s%c%c%s%c%c",
|
||||||
m_callsign.c_str(), server.c_str(),
|
m_callsign.c_str(), server.c_str(),
|
||||||
lat, (rawLatitude < 0.0F) ? 'S' : 'N',
|
lat, (rawLatitude < 0.0F) ? 'S' : 'N', symbol[0],
|
||||||
lon, (rawLongitude < 0.0F) ? 'W' : 'E');
|
lon, (rawLongitude < 0.0F) ? 'W' : 'E',symbol[0]);
|
||||||
|
|
||||||
if (bearingSet && velocitySet)
|
if (bearingSet && velocitySet)
|
||||||
::sprintf(output + ::strlen(output), "%03.0f/%03.0f", rawBearing, rawVelocity * 0.539957F);
|
::sprintf(output + ::strlen(output), "%03.0f/%03.0f", rawBearing, rawVelocity * 0.539957F);
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
|
|
||||||
bool open();
|
bool open();
|
||||||
|
|
||||||
void setInfo(unsigned int txFrequency, unsigned int rxFrequency, const std::string& desc);
|
void setInfo(unsigned int txFrequency, unsigned int rxFrequency, const std::string& desc, const std::string& symbol);
|
||||||
|
|
||||||
void setStaticLocation(float latitude, float longitude, int height);
|
void setStaticLocation(float latitude, float longitude, int height);
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ private:
|
||||||
float m_longitude;
|
float m_longitude;
|
||||||
int m_height;
|
int m_height;
|
||||||
std::string m_desc;
|
std::string m_desc;
|
||||||
|
std::string m_symbol;
|
||||||
sockaddr_storage m_aprsAddr;
|
sockaddr_storage m_aprsAddr;
|
||||||
unsigned int m_aprsAddrLen;
|
unsigned int m_aprsAddrLen;
|
||||||
CUDPSocket m_aprsSocket;
|
CUDPSocket m_aprsSocket;
|
||||||
|
|
|
@ -72,6 +72,7 @@ m_aprsAddress("127.0.0.1"),
|
||||||
m_aprsPort(8673U),
|
m_aprsPort(8673U),
|
||||||
m_aprsSuffix(),
|
m_aprsSuffix(),
|
||||||
m_aprsDescription(),
|
m_aprsDescription(),
|
||||||
|
m_aprsSymbol(),
|
||||||
m_networkPort(0U),
|
m_networkPort(0U),
|
||||||
m_networkHosts1(),
|
m_networkHosts1(),
|
||||||
m_networkHosts2(),
|
m_networkHosts2(),
|
||||||
|
@ -235,6 +236,8 @@ bool CConf::read()
|
||||||
m_aprsSuffix = value;
|
m_aprsSuffix = value;
|
||||||
else if (::strcmp(key, "Description") == 0)
|
else if (::strcmp(key, "Description") == 0)
|
||||||
m_aprsDescription = value;
|
m_aprsDescription = value;
|
||||||
|
else if (::strcmp(key, "Symbol") == 0)
|
||||||
|
m_aprsSymbol = value;
|
||||||
} else if (section == SECTION_NETWORK) {
|
} else if (section == SECTION_NETWORK) {
|
||||||
if (::strcmp(key, "Port") == 0)
|
if (::strcmp(key, "Port") == 0)
|
||||||
m_networkPort = (unsigned short)::atoi(value);
|
m_networkPort = (unsigned short)::atoi(value);
|
||||||
|
@ -415,6 +418,11 @@ std::string CConf::getAPRSDescription() const
|
||||||
return m_aprsDescription;
|
return m_aprsDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CConf::getAPRSSymbol() const
|
||||||
|
{
|
||||||
|
return m_aprsSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int CConf::getLogDisplayLevel() const
|
unsigned int CConf::getLogDisplayLevel() const
|
||||||
{
|
{
|
||||||
return m_logDisplayLevel;
|
return m_logDisplayLevel;
|
||||||
|
|
|
@ -65,6 +65,7 @@ public:
|
||||||
unsigned short getAPRSPort() const;
|
unsigned short getAPRSPort() const;
|
||||||
std::string getAPRSSuffix() const;
|
std::string getAPRSSuffix() const;
|
||||||
std::string getAPRSDescription() const;
|
std::string getAPRSDescription() const;
|
||||||
|
std::string getAPRSSymbol() const;
|
||||||
|
|
||||||
// The Log section
|
// The Log section
|
||||||
unsigned int getLogDisplayLevel() const;
|
unsigned int getLogDisplayLevel() const;
|
||||||
|
@ -134,6 +135,7 @@ private:
|
||||||
unsigned short m_aprsPort;
|
unsigned short m_aprsPort;
|
||||||
std::string m_aprsSuffix;
|
std::string m_aprsSuffix;
|
||||||
std::string m_aprsDescription;
|
std::string m_aprsDescription;
|
||||||
|
std::string m_aprsSymbol;
|
||||||
|
|
||||||
unsigned short m_networkPort;
|
unsigned short m_networkPort;
|
||||||
std::string m_networkHosts1;
|
std::string m_networkHosts1;
|
||||||
|
|
|
@ -102,8 +102,8 @@ unsigned int CIcomNetwork::read(unsigned char* data)
|
||||||
if (length <= 0)
|
if (length <= 0)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
if (!CUDPSocket::match(m_addr, addr)) {
|
if (!CUDPSocket::match(m_addr, addr, IMT_ADDRESS_ONLY)) {
|
||||||
LogWarning("Icom Data received from an unknown address or port");
|
LogWarning("Icom Data received from an unknown address");
|
||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@ CC = cc
|
||||||
CXX = c++
|
CXX = c++
|
||||||
|
|
||||||
# Use the following CFLAGS and LIBS if you don't want to use gpsd.
|
# Use the following CFLAGS and LIBS if you don't want to use gpsd.
|
||||||
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUDP_SOCKET_MAX=2 -std=c++0x -pthread
|
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -std=c++0x -pthread
|
||||||
LIBS = -lpthread
|
LIBS = -lpthread
|
||||||
|
|
||||||
# Use the following CFLAGS and LIBS if you do want to use gpsd.
|
# Use the following CFLAGS and LIBS if you do want to use gpsd.
|
||||||
#CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUDP_SOCKET_MAX=2 -DUSE_GPSD -std=c++0x -pthread
|
#CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUSE_GPSD -std=c++0x -pthread
|
||||||
#LIBS = -lpthread -lgps
|
#LIBS = -lpthread -lgps
|
||||||
|
|
||||||
LDFLAGS = -g
|
LDFLAGS = -g
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -312,9 +313,11 @@ void CNXDNGateway::run()
|
||||||
hangTimer.start();
|
hangTimer.start();
|
||||||
}
|
}
|
||||||
} else if (currentTG == 0U) {
|
} else if (currentTG == 0U) {
|
||||||
// Don't pass reflector control data through to the MMDVM
|
bool poll = false;
|
||||||
if (::memcmp(buffer, "NXDND", 5U) == 0) {
|
|
||||||
// Find the static TG that this audio data belongs to
|
// We weren't really connected yet, but we got a reply from a poll, or some data
|
||||||
|
if ((::memcmp(buffer, "NXDND", 5U) == 0) || (poll = (::memcmp(buffer, "NXDNP", 5U) == 0))) {
|
||||||
|
// Find the static TG that this audio data/poll belongs to
|
||||||
for (std::vector<CStaticTG>::const_iterator it = staticTGs.cbegin(); it != staticTGs.cend(); ++it) {
|
for (std::vector<CStaticTG>::const_iterator it = staticTGs.cbegin(); it != staticTGs.cend(); ++it) {
|
||||||
if (CUDPSocket::match(addr, (*it).m_addr)) {
|
if (CUDPSocket::match(addr, (*it).m_addr)) {
|
||||||
currentTG = (*it).m_tg;
|
currentTG = (*it).m_tg;
|
||||||
|
@ -333,7 +336,7 @@ void CNXDNGateway::run()
|
||||||
|
|
||||||
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
||||||
|
|
||||||
if (grp && currentTG == dstTG)
|
if (grp && currentTG == dstTG && !poll)
|
||||||
localNetwork->write(buffer + 10U, len - 10U);
|
localNetwork->write(buffer + 10U, len - 10U);
|
||||||
|
|
||||||
LogMessage("Switched to reflector %u due to network activity", currentTG);
|
LogMessage("Switched to reflector %u due to network activity", currentTG);
|
||||||
|
@ -539,6 +542,21 @@ void CNXDNGateway::run()
|
||||||
voice->linkedTo(currentTG);
|
voice->linkedTo(currentTG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (::memcmp(buffer + 0U, "status", 6U) == 0) {
|
||||||
|
std::string state = std::string("nxdn:") + ((currentAddrLen > 0) ? "conn" : "disc");
|
||||||
|
remoteSocket->write((unsigned char*)state.c_str(), (unsigned int)state.length(), addr, addrLen);
|
||||||
|
} else if (::memcmp(buffer + 0U, "host", 4U) == 0) {
|
||||||
|
std::string ref;
|
||||||
|
|
||||||
|
if (currentAddrLen > 0) {
|
||||||
|
char buffer[INET6_ADDRSTRLEN];
|
||||||
|
if (getnameinfo((struct sockaddr*)¤tAddr, currentAddrLen, buffer, sizeof(buffer), 0, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||||
|
ref = std::string(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string host = std::string("nxdn:\"") + ((ref.length() == 0) ? "NONE" : ref) + "\"";
|
||||||
|
remoteSocket->write((unsigned char*)host.c_str(), (unsigned int)host.length(), addr, addrLen);
|
||||||
} else {
|
} else {
|
||||||
CUtils::dump("Invalid remote command received", buffer, res);
|
CUtils::dump("Invalid remote command received", buffer, res);
|
||||||
}
|
}
|
||||||
|
@ -637,8 +655,9 @@ void CNXDNGateway::createGPS()
|
||||||
unsigned int txFrequency = m_conf.getTxFrequency();
|
unsigned int txFrequency = m_conf.getTxFrequency();
|
||||||
unsigned int rxFrequency = m_conf.getRxFrequency();
|
unsigned int rxFrequency = m_conf.getRxFrequency();
|
||||||
std::string desc = m_conf.getAPRSDescription();
|
std::string desc = m_conf.getAPRSDescription();
|
||||||
|
std::string symbol = m_conf.getAPRSSymbol();
|
||||||
|
|
||||||
m_writer->setInfo(txFrequency, rxFrequency, desc);
|
m_writer->setInfo(txFrequency, rxFrequency, desc, symbol);
|
||||||
|
|
||||||
bool enabled = m_conf.getGPSDEnabled();
|
bool enabled = m_conf.getGPSDEnabled();
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ Address=127.0.0.1
|
||||||
Port=8673
|
Port=8673
|
||||||
Suffix=N
|
Suffix=N
|
||||||
Description=APRS Description
|
Description=APRS Description
|
||||||
|
# Symbol="/r"
|
||||||
|
|
||||||
[Id Lookup]
|
[Id Lookup]
|
||||||
Name=NXDN.csv
|
Name=NXDN.csv
|
||||||
|
|
|
@ -3,45 +3,96 @@
|
||||||
# The format of this file is the number of the Talk Group followed by the host name or address and port
|
# The format of this file is the number of the Talk Group followed by the host name or address and port
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# 14 www.spanish BY radiomania.net
|
||||||
|
14 nxdn14.nxdn.es 41400
|
||||||
|
|
||||||
|
# 38 Thailand
|
||||||
|
38 nxdn38.kozow.com 41400
|
||||||
|
|
||||||
|
# 112 Emergencias EA
|
||||||
|
112 112.nxdn.es 41401
|
||||||
|
|
||||||
|
# 137 Thailand - USA XLX Bridge
|
||||||
|
137 nxdn137.mywire.org 41400
|
||||||
|
|
||||||
# 138 Thailand
|
# 138 Thailand
|
||||||
138 nxdn138.freeddns.org 41400
|
138 nxdn138.freeddns.org 41400
|
||||||
|
|
||||||
|
# 148 Thailand Link TO XLX148 Module X
|
||||||
|
148 nxdn.hs3tdi.com 41400
|
||||||
|
|
||||||
# 149 Thailand
|
# 149 Thailand
|
||||||
149 nxdn.pwk.ac.th 41400
|
149 nxdn.pwk.ac.th 41400
|
||||||
|
|
||||||
# 202 HELLAS Zone
|
# 202 HELLAS Zone
|
||||||
202 hellaszone.com 41400
|
202 hellaszone.com 41400
|
||||||
|
|
||||||
|
# 204 Netherlands
|
||||||
|
204 nxdn.repeaters-haaglanden.nl 41400
|
||||||
|
|
||||||
# 214 RED SPAIN
|
# 214 RED SPAIN
|
||||||
214 nxdn214.xreflector.es 41400
|
214 214nxdn.nxdn.es 41401
|
||||||
|
|
||||||
|
# 224 RED SPAIN
|
||||||
|
224 nxdn224.nxdn.es 41401
|
||||||
|
|
||||||
# 226 Romania YO NXDN Network
|
# 226 Romania YO NXDN Network
|
||||||
226 nxdn.dstar-yo.ro 41400
|
226 nxdn.dstar-yo.ro 41400
|
||||||
|
|
||||||
|
# 235 DVSPH UK Wide
|
||||||
|
235 nxdn.dvsph.net 41400
|
||||||
|
|
||||||
# 260 HBLink Poland
|
# 260 HBLink Poland
|
||||||
260 nxdn.hblink.pl 41400
|
260 nxdn.hblink.pl 41400
|
||||||
|
|
||||||
|
# 280 Cyprus-NXDN Cyprus
|
||||||
|
280 nxdn.greece.freedmr.online 41404
|
||||||
|
|
||||||
# 302 P25 Canada
|
# 302 P25 Canada
|
||||||
302 p25canada.hopto.org 41400
|
302 p25canada.hopto.org 41400
|
||||||
|
|
||||||
|
# 401 CT-RI-NXDN
|
||||||
|
401 nxdn.mooo.com 41401
|
||||||
|
|
||||||
# 420 Super Freq
|
# 420 Super Freq
|
||||||
420 nxdn.superfreqdigital.com 41400
|
420 nxdn.superfreqdigital.com 41400
|
||||||
|
|
||||||
# 530 New Zealand
|
# 530 New Zealand
|
||||||
530 zldigitalreflectors.hopto.org 41400
|
530 zldigitalreflectors.hopto.org 41400
|
||||||
|
|
||||||
# 545 XLX545E PA
|
# 545 XLX545E PA
|
||||||
545 70.44.20.24 41401
|
545 70.44.20.24 41401
|
||||||
|
|
||||||
|
# 621 HRCC LINK
|
||||||
|
621 urf.hrcc.link 41400
|
||||||
|
|
||||||
# 707 RuralMN Reflector
|
# 707 RuralMN Reflector
|
||||||
707 707nxdn.kd0ioe.com 41400
|
707 707nxdn.kd0ioe.com 41400
|
||||||
|
|
||||||
|
# 711 NXDN NL
|
||||||
|
711 nxdn1.digitalevoice.nl 41400
|
||||||
|
|
||||||
|
# 712 NXDN JP
|
||||||
|
712 nxdn.xreflector-jp.org 41400
|
||||||
|
|
||||||
# 730 Chile NXDN Reflector to BM TG 730
|
# 730 Chile NXDN Reflector to BM TG 730
|
||||||
730 sdradio.cl 41400
|
730 sdradio.cl 41400
|
||||||
|
|
||||||
|
# 805 US RocketCity-AL-NXDN Bridge
|
||||||
|
805 45.32.166.46 41400
|
||||||
|
|
||||||
# 822 Thailand NXDN to BM TG 520822
|
# 822 Thailand NXDN to BM TG 520822
|
||||||
822 nxdn.dstarthailand.com 41400
|
822 nxdn.dstarthailand.com 41400
|
||||||
|
|
||||||
|
# 841 Team Wave NXDN Bridge
|
||||||
|
841 mx0wvv.ddns.net 41400
|
||||||
|
|
||||||
|
# 847 Chandler Hams
|
||||||
|
847 xlx847.kk7mnz.com 41400
|
||||||
|
|
||||||
|
# 848 Chandler Hams
|
||||||
|
848 xlx848.kk7mnz.com 41400
|
||||||
|
|
||||||
# 910 Washington DC - Virginia - Maryland
|
# 910 Washington DC - Virginia - Maryland
|
||||||
910 nxdn910.freeddns.org 41400
|
910 nxdn910.freeddns.org 41400
|
||||||
|
|
||||||
|
@ -49,25 +100,28 @@
|
||||||
911 nxdn.k2cop.com 41400
|
911 nxdn.k2cop.com 41400
|
||||||
|
|
||||||
# 914 Latin America
|
# 914 Latin America
|
||||||
914 80.211.94.145 41400
|
914 914.nxdn.es 41400
|
||||||
|
|
||||||
|
# 926 HamFurs/LoFAR MultiMode Bridge
|
||||||
|
926 urf.kf3rry.org 41400
|
||||||
|
|
||||||
# 1007 Harley-Hangout TGIF TG-1007 Bridge
|
# 1007 Harley-Hangout TGIF TG-1007 Bridge
|
||||||
1007 43773.kb5rir.com 41400
|
1007 43773.kb5rir.com 41400
|
||||||
|
|
||||||
# 1200 Florida
|
# 1200 Florida
|
||||||
1200 florida.nxref.org 41400
|
1200 florida.nxref.org 41400
|
||||||
|
|
||||||
# 2140 DMRplus BM Multi
|
# 2020 GR-GREECE-DV-NXDN
|
||||||
2140 94.177.235.81 41400
|
2020 nxdn.greece.freedmr.online 41400
|
||||||
|
|
||||||
# 2225 IT Tuscany
|
# 2029 GR-Crete-DV-NXDN
|
||||||
2225 80.211.99.134 41400
|
2029 nxdn.greece.freedmr.online 41402
|
||||||
|
|
||||||
# 2231 IT Multiprotocollo Sardegna
|
# 2140 Treehouse NXDN
|
||||||
2231 nxdn.is0.org 41400
|
2140 2140.nxdn.es 41402
|
||||||
|
|
||||||
# 2241 IT TUSCANY MULTIP
|
# 2147 Andalucia Room
|
||||||
2241 nxdn.grupporadiofirenze.net 41400
|
2147 2147.nxdn.es 41400
|
||||||
|
|
||||||
# 2249 IT SICILIA
|
# 2249 IT SICILIA
|
||||||
2249 nxdn.digitalsicilia.it 41400
|
2249 nxdn.digitalsicilia.it 41400
|
||||||
|
@ -78,6 +132,12 @@
|
||||||
# 2265 Baia Mare YO5 NXDN Network
|
# 2265 Baia Mare YO5 NXDN Network
|
||||||
2265 nxdn.dstar-yo.ro 41405
|
2265 nxdn.dstar-yo.ro 41405
|
||||||
|
|
||||||
|
# 2345 CQ-North UK
|
||||||
|
2345 nxdn.cqnorth.org.uk 41400
|
||||||
|
|
||||||
|
# 2350 UK ChatterBOX
|
||||||
|
2350 nxdn2350.freestar.network 41401
|
||||||
|
|
||||||
# 2503 RU DMR
|
# 2503 RU DMR
|
||||||
2503 nxdn.r1ik.ru 41400
|
2503 nxdn.r1ik.ru 41400
|
||||||
|
|
||||||
|
@ -88,37 +148,73 @@
|
||||||
2724 dmr.nrsi.ie 41400
|
2724 dmr.nrsi.ie 41400
|
||||||
|
|
||||||
# 3023 Ontario Crosslink
|
# 3023 Ontario Crosslink
|
||||||
3023 ontxlink.hopto.org 41400
|
3023 ontxlink.hopto.org 41400
|
||||||
|
|
||||||
|
# 3120 LKDVM NXDN
|
||||||
|
3120 lkdvm.nxdn.es 41400
|
||||||
|
|
||||||
# 3142 Pennsylvania
|
# 3142 Pennsylvania
|
||||||
3142 3.215.215.169 41402
|
3142 3.215.215.169 41402
|
||||||
|
|
||||||
|
# 3160 K6JWN Crossroads
|
||||||
|
3160 nxdn.k6jwn.org 41400
|
||||||
|
|
||||||
# PRadise
|
# PRadise
|
||||||
3300 nxdn3300.from-pr.com 41400
|
3300 nxdn3300.from-pr.com 41400
|
||||||
|
|
||||||
# 3400 W2BTN Group
|
# PT RICO LINK
|
||||||
3400 w2btn.dmrnet.net 41400
|
3306 3306.nxdn.es 41400
|
||||||
|
|
||||||
|
# 3400 3400 Digital Group (NJ Multimode Reflector)
|
||||||
|
3400 3400.ddns.net 41400
|
||||||
|
|
||||||
# 5057 VK7 TAS
|
# 5057 VK7 TAS
|
||||||
5057 45.248.50.37 41400
|
5057 45.248.50.37 41400
|
||||||
|
|
||||||
|
# 7144 Chiriqui Link Panama
|
||||||
|
7144 nxdn.pa7lim.nl 41400
|
||||||
|
|
||||||
# 7160 Peru Digital
|
# 7160 Peru Digital
|
||||||
7160 nxdn.dmr-peru.pe 41400
|
7160 nxdn.dmr-peru.net 41400
|
||||||
|
|
||||||
|
# 7221 FreeDMR Argentina
|
||||||
|
7221 nxdn.freedmr.ar 41400
|
||||||
|
|
||||||
# 7225 Multiprotocolo Argentina
|
# 7225 Multiprotocolo Argentina
|
||||||
7225 argnxdn.ddnsfree.com 41400
|
7225 argnxdn.ddnsfree.com 41400
|
||||||
|
|
||||||
|
# 7240 MASTER-SUL Brasil
|
||||||
|
7240 nxdn.freedmr-brasil.qsl.br 41400
|
||||||
|
|
||||||
|
# 7245 Brasil Multiprotocolo
|
||||||
|
7245 pu4ron.dynv6.net 41400
|
||||||
|
|
||||||
# 7309 CHILE-LINK NXDN Reflector to BM TG 73099
|
# 7309 CHILE-LINK NXDN Reflector to BM TG 73099
|
||||||
7309 sdradio.cl 41401
|
7309 sdradio.cl 41401
|
||||||
|
|
||||||
|
# 7487 Uruguay Link
|
||||||
|
7487 uruguay.nxdn.es 41400
|
||||||
|
|
||||||
|
# 7941 White Mountain Repeater Association
|
||||||
|
7941 wmra-nxdn.mooo.com 41400
|
||||||
|
|
||||||
|
# 8465 Ecuador
|
||||||
|
8465 jjsc.ddns.net 41400
|
||||||
|
|
||||||
# 9480 ICQPODCAST - ICQ DIGITAL GROUP
|
# 9480 ICQPODCAST - ICQ DIGITAL GROUP
|
||||||
9480 reflectors.icqpodcast.com 42400
|
9480 46.101.36.246 41400
|
||||||
|
|
||||||
|
# 9517 M17 Project - M17-M17 C Reflector bridge
|
||||||
|
9517 112.213.34.65 41401
|
||||||
|
|
||||||
# 9846 NXDN Portal to WW8GM YSF network www.gmarc.org
|
# 9846 NXDN Portal to WW8GM YSF network www.gmarc.org
|
||||||
9846 nxdn.dudetronics.com 41401
|
9846 nxdn.dudetronics.com 41401
|
||||||
|
|
||||||
# 10200 North America
|
# 10200 North America
|
||||||
10200 dvswitch.org 42400
|
10200 dvswitch.org 42400
|
||||||
|
|
||||||
|
# 10201 North America TAC
|
||||||
|
10201 10201.dyndns.org 41400
|
||||||
|
|
||||||
# 10301 HBLINK Espana
|
# 10301 HBLINK Espana
|
||||||
10301 ea5gvk.duckdns.org 41400
|
10301 ea5gvk.duckdns.org 41400
|
||||||
|
@ -135,51 +231,69 @@
|
||||||
# 10922 CQ-UK
|
# 10922 CQ-UK
|
||||||
10922 81.150.10.62 41400
|
10922 81.150.10.62 41400
|
||||||
|
|
||||||
# 17000 M17 Project
|
# 11069 KK6RQ NXDN Reflector
|
||||||
17000 107.191.48.144 41400
|
11069 area52.zapto.org 41400
|
||||||
|
|
||||||
|
# 17603 URFM17 Universal Reflector
|
||||||
|
17603 urf.m17.link 41400
|
||||||
|
|
||||||
# 20000 Europe, German speaking
|
# 20000 Europe, German speaking
|
||||||
20000 89.185.97.38 41400
|
20000 89.185.97.38 41400
|
||||||
|
|
||||||
# 20201 NXDN Athens GR HBLink
|
# 20201 NXDN Athens GR HBLink
|
||||||
20201 stargate-dmr.net 41400
|
20201 stargate-dmr.net 41400
|
||||||
|
|
||||||
|
# 20211 GR-Lamia-DV-NXDN
|
||||||
|
20211 nxdn.greece.freedmr.online 41403
|
||||||
|
|
||||||
|
# 20281 GR-KERKYRA-DV-NXDN
|
||||||
|
20281 nxdn.greece.freedmr.online 41401
|
||||||
|
|
||||||
|
# 20222 NXDN HELLAS-FRN
|
||||||
|
20222 greece-frn.ddns.net 41400
|
||||||
|
|
||||||
# 20328 NXDN German Pegasus Project
|
# 20328 NXDN German Pegasus Project
|
||||||
20328 nxdn.projekt-pegasus.net 41400
|
20328 nxdn.projekt-pegasus.net 41400
|
||||||
|
|
||||||
# 20330 NXDN German DE-RAMSES Project
|
# 20330 NXDN German DE-RAMSES Project
|
||||||
20330 161.97.140.131 41400
|
20330 81.169.173.132 41400
|
||||||
|
|
||||||
# 20333 NXDN LausitzLink Germany
|
|
||||||
20333 nxdn02.bzsax.de 41400
|
|
||||||
|
|
||||||
# 20421 DL-Nordwest (dl-nordwest.com) by 9V1LH/DG1BGS and DK5BS
|
# 20421 DL-Nordwest (dl-nordwest.com) by 9V1LH/DG1BGS and DK5BS
|
||||||
20421 9v1lh.spdns.org 41400
|
20421 dl-nordwest.com 41400
|
||||||
|
|
||||||
|
# 20424 Rijmond regio Rotterdam
|
||||||
|
20424 nxdn.pa7lim.nl 41400
|
||||||
|
|
||||||
|
# 20426 Boreft regio Bodegraven
|
||||||
|
20426 pa8f.nl 41400
|
||||||
|
|
||||||
# 20945 Deutschland DL1BH
|
# 20945 Deutschland DL1BH
|
||||||
20945 dl1bh.ddns.net 41400
|
20945 dl1bh.ddns.net 41400
|
||||||
|
|
||||||
# 21410 NXDN SPAIN
|
# 21426 FreeSTAR SPAIN
|
||||||
21410 nxdn21410.nxdn.es 41400
|
21426 nxdn.pa7lim.nl 41400
|
||||||
|
|
||||||
# 2147 Andalucia Room
|
# 21430 EA5URM-Murcia
|
||||||
21401 2147.nxdn.es 41400
|
21430 ea5urm-murcia.duckdns.org 41400
|
||||||
|
|
||||||
# 21418 NXDN RC Veleta
|
|
||||||
21418 rcveleta.xreflector.es 41400
|
|
||||||
|
|
||||||
# 21430 Alcayna
|
|
||||||
21430 alcayna.duckdns.org 41400
|
|
||||||
|
|
||||||
# 21465 ADER Multimode
|
# 21465 ADER Multimode
|
||||||
21465 aderdigital.ddns.net 41400
|
21465 aderdigital.ddns.net 41400
|
||||||
|
|
||||||
|
# 21468 Canarias NXDN
|
||||||
|
21468 nxdn.pa7lim.nl 41400
|
||||||
|
|
||||||
|
# 21909 PRIDE - LGBTQIA+ Hams
|
||||||
|
21909 ham.kimberlychase.com 41400
|
||||||
|
|
||||||
# 22200 IT HBLINK ITALY
|
# 22200 IT HBLINK ITALY
|
||||||
22200 nxdn.hblink.it 41400
|
22200 nxdn.hblink.it 41400
|
||||||
|
|
||||||
# 22202 IT SARDINIA
|
# 22202 IT SARDINIA
|
||||||
22202 87.106.152.249 41400
|
22202 87.106.152.249 41400
|
||||||
|
|
||||||
|
# 22209 IT Radio Chat NXDN
|
||||||
|
22209 it-radiochat.ddns.net 41400
|
||||||
|
|
||||||
# 22220 IT ItalyNet Conference
|
# 22220 IT ItalyNet Conference
|
||||||
22220 198.245.55.177 41400
|
22220 198.245.55.177 41400
|
||||||
|
|
||||||
|
@ -189,38 +303,56 @@
|
||||||
# 22245 IT PIEDMONT GDO
|
# 22245 IT PIEDMONT GDO
|
||||||
22245 nxdngdo.duckdns.org 41400
|
22245 nxdngdo.duckdns.org 41400
|
||||||
|
|
||||||
|
# 22251 IT TG22251 TOSCANA
|
||||||
|
22251 nxdn5.grupporadiofirenze.net 41400
|
||||||
|
|
||||||
# 22252 IT MULTIPROTOCOL NETWORK
|
# 22252 IT MULTIPROTOCOL NETWORK
|
||||||
22252 46.226.178.80 41400
|
22252 46.226.178.80 41400
|
||||||
|
|
||||||
# 22258 D2ALP HBLink Italia
|
# 22258 D2ALP HBLink Italia
|
||||||
22258 94.177.173.53 41400
|
22258 94.177.173.53 41400
|
||||||
|
|
||||||
# 22292 IT MP XLX039/B BM2222
|
# 22292 Italy-MP TG 22292 BM
|
||||||
22292 hblink.dmrbrescia.it 41400
|
22292 nxdn.grupporadiofirenze.net 41400
|
||||||
|
|
||||||
|
# 22488 Italy Centro
|
||||||
|
22488 185.203.119.229 41400
|
||||||
|
|
||||||
# 22825 Swiss NXDN Reflerctor
|
# 22825 Swiss NXDN Reflerctor
|
||||||
22825 212.237.33.114 41400
|
22825 212.237.33.114 41400
|
||||||
|
|
||||||
|
# 23416 400Club
|
||||||
|
23416 nxdn23416.freestar.network 41402
|
||||||
|
|
||||||
# 23426 FreeSTAR UK
|
# 23426 FreeSTAR UK
|
||||||
23426 nxdn.freestar.network 41400
|
23426 nxdn.freestar.network 41400
|
||||||
|
|
||||||
# 22488 Digital Network
|
# 23491 NXDN Cambridge
|
||||||
22488 185.203.119.229 41400
|
23491 nxdn-cambridge.dyndns.org 41400
|
||||||
|
|
||||||
# 23530 Yorkshire UK, https://dvswitch.uk/nxdn
|
# 23511 LEFARS Multi-Reflector
|
||||||
23530 194.146.44.13 41400
|
23511 xlxlef.gb7hh.co.uk 41400
|
||||||
|
|
||||||
|
# 23525 Extended Freedom Network M0XFN
|
||||||
|
23525 nxdn.m0xfn.radio 41401
|
||||||
|
|
||||||
|
# 23530 CQ-UK-North, http://109.228.47.204/nxdn
|
||||||
|
23530 109.228.47.204 41400
|
||||||
|
|
||||||
# 23551 NXDN Scotland
|
# 23551 NXDN Scotland
|
||||||
23551 nxdnscotland.ddns.net 41400
|
23551 nxdnscotland.ddns.net 41400
|
||||||
|
|
||||||
|
# 23556 DVSPH Multimode
|
||||||
|
23556 xlx600.dvsph.net 41400
|
||||||
|
|
||||||
# 23595 OZ-DMR_CHAT
|
# 23595 OZ-DMR_CHAT
|
||||||
23595 reflectors.oz-dmr.uk 41400
|
23595 nxdn.oz-dmr.network 41400
|
||||||
|
|
||||||
# 25000 NX Core
|
# 25000 NX Core
|
||||||
25000 173.166.94.77 41400
|
25000 173.166.94.77 41400
|
||||||
|
|
||||||
# 25577 UA Azimuth
|
# 25577 UA Azimuth
|
||||||
25577 178.136.234.51 41400
|
25577 nxdn.ur4wwr.org 41400
|
||||||
|
|
||||||
# 25617 Russia Kavkaz
|
# 25617 Russia Kavkaz
|
||||||
25617 kavkaz.qrz.ru 41400
|
25617 kavkaz.qrz.ru 41400
|
||||||
|
@ -229,7 +361,7 @@
|
||||||
25641 194.182.85.217 41400
|
25641 194.182.85.217 41400
|
||||||
|
|
||||||
# 26000 Poland
|
# 26000 Poland
|
||||||
26000 80.211.249.221 41400
|
26000 185.174.14.75 41400
|
||||||
|
|
||||||
# 26078 Poland HBLink Network
|
# 26078 Poland HBLink Network
|
||||||
26078 nxdn.hblink.kutno.pl 41400
|
26078 nxdn.hblink.kutno.pl 41400
|
||||||
|
@ -241,7 +373,7 @@
|
||||||
26085 91.245.81.2 41400
|
26085 91.245.81.2 41400
|
||||||
|
|
||||||
# 26200 Deutschland
|
# 26200 Deutschland
|
||||||
26200 135.125.204.81 41400
|
26200 dl.nxdn.eu 41400
|
||||||
|
|
||||||
# 26810 Portuguese speakers
|
# 26810 Portuguese speakers
|
||||||
26810 nxcore.brandmeister.pt 41400
|
26810 nxcore.brandmeister.pt 41400
|
||||||
|
@ -250,7 +382,7 @@
|
||||||
28299 arcnxdn.duckdns.org 41400
|
28299 arcnxdn.duckdns.org 41400
|
||||||
|
|
||||||
# 29999 AllStarLink Bridge
|
# 29999 AllStarLink Bridge
|
||||||
29999 64.154.38.103 41400
|
29999 64.154.38.103 41400
|
||||||
|
|
||||||
# 30639 NorCal-Bridge / Multimode-NXDN
|
# 30639 NorCal-Bridge / Multimode-NXDN
|
||||||
30639 nfonorcalnxdn.dyndns.org 41400
|
30639 nfonorcalnxdn.dyndns.org 41400
|
||||||
|
@ -261,39 +393,48 @@
|
||||||
# 31031 XLX310 Multimode Reflector
|
# 31031 XLX310 Multimode Reflector
|
||||||
31031 ref.xlx310.com 41400
|
31031 ref.xlx310.com 41400
|
||||||
|
|
||||||
|
# 31059 Vidalia Net NXDN
|
||||||
|
31059 vidalianet.cbridge.net 41400
|
||||||
|
|
||||||
# 31081 Colorado Fun Machine WE0FUN Bridge to C4FM, DMR, DStar, P25 and AllStarLink (Analog) http://www.we0fun.com
|
# 31081 Colorado Fun Machine WE0FUN Bridge to C4FM, DMR, DStar, P25 and AllStarLink (Analog) http://www.we0fun.com
|
||||||
31081 nxdn.we0fun.com 41400
|
31081 nxdn.we0fun.com 41400
|
||||||
|
|
||||||
# 31088 Colorado HD
|
|
||||||
31088 54.191.50.212 41400
|
|
||||||
|
|
||||||
# 31092 Connecticut Chat
|
|
||||||
31092 nxdn.alecwasserman.com 41400
|
|
||||||
|
|
||||||
# 31121 First Coast FL
|
# 31121 First Coast FL
|
||||||
31121 dvse.dmrnet.net 41400
|
31121 dvse.dmrnet.net 41400
|
||||||
|
|
||||||
# 31137 Kingsland Digital Link
|
# 31137 Kingsland Digital Link
|
||||||
31137 74.91.119.94 41400
|
31137 74.91.119.94 41400
|
||||||
|
|
||||||
|
# 31138 Kingsland Local Ga
|
||||||
|
31138 kingsland.cbridge.net 41400
|
||||||
|
|
||||||
# 31171 Illinois Link
|
# 31171 Illinois Link
|
||||||
31171 illink.radiotechnology.xyz 41400
|
31171 illink.radiotechnology.xyz 41400
|
||||||
|
|
||||||
|
# 31177 WESDIG Western US Digital Group
|
||||||
|
31177 nxdn.wesdig.com 41400
|
||||||
|
|
||||||
# 31188 Southern Indiana
|
# 31188 Southern Indiana
|
||||||
31188 w9windigital.org 41400
|
31188 w9windigital.org 41400
|
||||||
|
|
||||||
# 31226 WorldWide Dx System
|
# 31226 WorldWide Dx System
|
||||||
31226 198.245.53.107 41400
|
31226 74.208.163.225 41400
|
||||||
|
|
||||||
|
# 31238 Foxhole Radio Bridged Network
|
||||||
|
31238 nxdn.foxhole.radio 41400
|
||||||
|
|
||||||
# 31257 NEARC
|
# 31257 NEARC
|
||||||
31257 nxdn.w0jay.com 41400
|
31257 nxdn.w0jay.com 41400
|
||||||
|
|
||||||
# 31264 XLX625 The BROniverse www.wa8bro.com
|
# 31264 Dudetronics NXDN
|
||||||
31264 nxdn.dudetronics.com 41400
|
31264 nxdn.dudetronics.com 41400
|
||||||
|
|
||||||
# 31340 Central New Jersey
|
# 31340 Central New Jersey
|
||||||
31340 cnjham.msmts.com 41400
|
31340 cnjham.msmts.com 41400
|
||||||
|
|
||||||
|
# 31399 OhioLink Network crossmode. http://olnradio.digital
|
||||||
|
31399 xlx.kd8grn.net 41400
|
||||||
|
|
||||||
# 31403 Oklahoma Link
|
# 31403 Oklahoma Link
|
||||||
31403 3.208.70.29 41400
|
31403 3.208.70.29 41400
|
||||||
|
|
||||||
|
@ -306,9 +447,15 @@
|
||||||
# 31444 Rhode Island Digital Link
|
# 31444 Rhode Island Digital Link
|
||||||
31444 18.219.32.21 41400
|
31444 18.219.32.21 41400
|
||||||
|
|
||||||
|
# 31490 utah bridge to dmr 314900
|
||||||
|
31490 utah00.utahdrn.org 41400
|
||||||
|
|
||||||
# 31581 KD2UQK Long Island, NY - NXDN Reflector
|
# 31581 KD2UQK Long Island, NY - NXDN Reflector
|
||||||
31581 kd2uqk.ham-radio-op.net 41400
|
31581 kd2uqk.ham-radio-op.net 41400
|
||||||
|
|
||||||
|
# 31582 KO4UYJ Milton, Fl - NXDN Reflector
|
||||||
|
31582 ko4uyj.com 41400
|
||||||
|
|
||||||
# 31665 TGIF Network
|
# 31665 TGIF Network
|
||||||
31665 tgif.network 41400
|
31665 tgif.network 41400
|
||||||
|
|
||||||
|
@ -324,47 +471,105 @@
|
||||||
# 32103 CW-Ops Academy
|
# 32103 CW-Ops Academy
|
||||||
32103 cwops.dyndns.org 41400
|
32103 cwops.dyndns.org 41400
|
||||||
|
|
||||||
|
# 33015 KP4CA Digital Network
|
||||||
|
33015 kp4ca-nxdn.ddns.net 41400
|
||||||
|
|
||||||
# 33581 OMISS Group
|
# 33581 OMISS Group
|
||||||
33581 omiss.dyndns.org 41400
|
33581 omiss.dyndns.org 41400
|
||||||
|
|
||||||
|
# 37030 SKYNET
|
||||||
|
37030 skynet.nxdn.es 41400
|
||||||
|
|
||||||
# 37224 Haiti NXDN
|
# 37224 Haiti NXDN
|
||||||
37224 44.98.254.130 41400
|
37224 44.98.254.130 41400
|
||||||
|
|
||||||
|
# 37500 Belarus Digital MultiMode Network
|
||||||
|
37500 xlx375.bfrr.by 41400
|
||||||
|
|
||||||
# 40721 Fusion Canada Fr /Wires-x /Ysf /Nxdn Network
|
# 40721 Fusion Canada Fr /Wires-x /Ysf /Nxdn Network
|
||||||
40721 158.69.169.205 41400
|
40721 158.69.169.205 41400
|
||||||
|
|
||||||
# 45000 Korea
|
|
||||||
45000 nxdn.dvham.com 41400
|
|
||||||
|
|
||||||
# 46000 China
|
|
||||||
46000 120.234.41.144 41400
|
|
||||||
|
|
||||||
# 43389 SouthEast Link
|
# 43389 SouthEast Link
|
||||||
43389 nxdn.lmarc.net 41400
|
43389 nxdn.lmarc.net 41400
|
||||||
|
|
||||||
|
# 44000 Japan
|
||||||
|
44000 nxdn44000.f5.si 41400
|
||||||
|
|
||||||
|
# 44120 Managed by JA2CCV
|
||||||
|
44120 nxdn.owari.biz 41400
|
||||||
|
|
||||||
|
# 44155 XLX298 YSF-NAGOYA BRIDGE NAGOYA JAPAN
|
||||||
|
44155 xrf298.mydns.jp 41400
|
||||||
|
|
||||||
|
# 45000 Korea
|
||||||
|
45000 nxdn.dvham.com 41400
|
||||||
|
|
||||||
|
# 45001 NXDN-DS5QDR Korea
|
||||||
|
45001 ds5qdr-mst.duckdns.org 41400
|
||||||
|
|
||||||
|
# 46000 China
|
||||||
|
46000 120.234.41.144 41400
|
||||||
|
|
||||||
|
# 46001 NXDN Link TG46001, China
|
||||||
|
46001 46001.bh1nyr.net 41400
|
||||||
|
|
||||||
|
# 46010 NXDN Link TG46010, Beijing, China
|
||||||
|
46010 46010.bh1nyr.net 41402
|
||||||
|
|
||||||
|
# 46516 NXDN Link TG46516, Xuzhou, China
|
||||||
|
46516 46516.bh1nyr.net 41404
|
||||||
|
|
||||||
# 50525 DMR TG50525 bridge
|
# 50525 DMR TG50525 bridge
|
||||||
50525 50525.nxref.org 41400
|
50525 50525.nxref.org 41400
|
||||||
|
|
||||||
|
# 50535 PrideNet multimode bridge
|
||||||
|
50535 p25tg50535.vkradio.com 41400
|
||||||
|
|
||||||
# 50599 BM NXCore bridge
|
# 50599 BM NXCore bridge
|
||||||
50599 nxdn.duckdns.org 41490
|
50599 nxdn.duckdns.org 41490
|
||||||
|
|
||||||
|
# 51500 DX8WMG-NXDN
|
||||||
|
51500 dx8wmg-ysf.ddns.net 41400
|
||||||
|
|
||||||
|
# 51547 KAPIHAN Network
|
||||||
|
51547 nxdn.kapihan.net 41400
|
||||||
|
|
||||||
# 52000 Thailand DTDXA XLX520N
|
# 52000 Thailand DTDXA XLX520N
|
||||||
52000 nxdn.dtdxa.com 41400
|
52000 nxdn.dtdxa.com 41400
|
||||||
|
|
||||||
# 52032 Surin Amateur Radio Association (HS3AS) ,Thailand,XLX149C
|
# 52032 Surin Amateur Radio Association (HS3AS) ,Thailand,XLX149C
|
||||||
52032 hs3as.pwk.ac.th 41404
|
52032 hs3as.pwk.ac.th 41404
|
||||||
|
|
||||||
|
# 52149 XLX149 D Thailand
|
||||||
|
52149 nxdn149d.pwk.ac.th 41408
|
||||||
|
|
||||||
|
# 52910 XLX Nexus
|
||||||
|
52910 nxdn910x.mywire.org 41400
|
||||||
|
|
||||||
# 53099 New Zealand, 53099 linked to BM TG 53099
|
# 53099 New Zealand, 53099 linked to BM TG 53099
|
||||||
53099 203.86.206.49 41400
|
53099 203.86.206.49 41400
|
||||||
|
|
||||||
|
# 60777 Who Cares ARG (URF239 A) https://whocaresradio.com/reflector
|
||||||
|
60777 urf239.whocaresradio.com 41400
|
||||||
|
|
||||||
|
# 63600 Latvian Digital MultiMode Network
|
||||||
|
63600 xlx.ham.lv 41400
|
||||||
|
|
||||||
|
# 64003 ELITE Group
|
||||||
|
64003 51.77.213.200 41400
|
||||||
|
|
||||||
# 65000 World Wide
|
# 65000 World Wide
|
||||||
65000 176.9.1.168 41400
|
65000 176.9.1.168 41400
|
||||||
|
|
||||||
# 65100 NXDN - 2007DXGROUP
|
# 65100 NXDN - 2007DXGROUP
|
||||||
65100 89.46.75.115 41400
|
65100 89.46.75.115 41400
|
||||||
|
|
||||||
# 65208 French-Test
|
# 65206 XLX210 YSF-BELGIUM M17BEL Bridges http://freedmr.be/nxdn
|
||||||
65208 m55.evxonline.net 41400
|
65206 161.97.169.233 41400
|
||||||
|
|
||||||
|
# 65208 French-NXDN
|
||||||
|
65208 nxdn.f5dan.fr 41400
|
||||||
|
|
||||||
# 65209 VARG http://www.varg.club
|
# 65209 VARG http://www.varg.club
|
||||||
65209 3.215.215.169 41410
|
65209 3.215.215.169 41410
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2014,2016,2018,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
CNXDNNetwork::CNXDNNetwork(unsigned short port, const std::string& callsign, bool debug) :
|
CNXDNNetwork::CNXDNNetwork(unsigned short port, const std::string& callsign, bool debug) :
|
||||||
m_callsign(callsign),
|
m_callsign(callsign),
|
||||||
m_socket(),
|
m_socket4(port),
|
||||||
m_port(port),
|
m_socket6(port),
|
||||||
m_debug(debug)
|
m_debug(debug)
|
||||||
{
|
{
|
||||||
assert(port > 0U);
|
assert(port > 0U);
|
||||||
|
@ -43,16 +43,17 @@ bool CNXDNNetwork::open()
|
||||||
{
|
{
|
||||||
LogInfo("Opening NXDN network connection");
|
LogInfo("Opening NXDN network connection");
|
||||||
|
|
||||||
unsigned int index = 0U;
|
sockaddr_storage addr4;
|
||||||
|
addr4.ss_family = AF_INET;
|
||||||
|
|
||||||
bool ret1 = m_socket.open(index, PF_INET, "", m_port);
|
bool ret = m_socket4.open(addr4);
|
||||||
if (ret1)
|
if (!ret)
|
||||||
index++;
|
return false;
|
||||||
|
|
||||||
bool ret2 = m_socket.open(index, PF_INET6, "", m_port);
|
sockaddr_storage addr6;
|
||||||
|
addr6.ss_family = AF_INET6;
|
||||||
|
|
||||||
// We're OK as long as we have either IPv4 or IPv6 or both.
|
return m_socket6.open(addr6);
|
||||||
return ret1 || ret2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNXDNNetwork::writeData(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen)
|
bool CNXDNNetwork::writeData(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen)
|
||||||
|
@ -96,7 +97,15 @@ bool CNXDNNetwork::writeData(const unsigned char* data, unsigned int length, uns
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
CUtils::dump(1U, "NXDN Network Data Sent", buffer, 43U);
|
CUtils::dump(1U, "NXDN Network Data Sent", buffer, 43U);
|
||||||
|
|
||||||
return m_socket.write(buffer, 43U, addr, addrLen);
|
switch (addr.ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
return m_socket4.write(buffer, 43U, addr, addrLen);
|
||||||
|
case AF_INET6:
|
||||||
|
return m_socket6.write(buffer, 43U, addr, addrLen);
|
||||||
|
default:
|
||||||
|
LogError("Unknown socket address family - %u", addr.ss_family);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNXDNNetwork::writePoll(const sockaddr_storage& addr, unsigned int addrLen, unsigned short tg)
|
bool CNXDNNetwork::writePoll(const sockaddr_storage& addr, unsigned int addrLen, unsigned short tg)
|
||||||
|
@ -118,7 +127,15 @@ bool CNXDNNetwork::writePoll(const sockaddr_storage& addr, unsigned int addrLen,
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
CUtils::dump(1U, "NXDN Network Poll Sent", data, 17U);
|
CUtils::dump(1U, "NXDN Network Poll Sent", data, 17U);
|
||||||
|
|
||||||
return m_socket.write(data, 17U, addr, addrLen);
|
switch (addr.ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
return m_socket4.write(data, 17U, addr, addrLen);
|
||||||
|
case AF_INET6:
|
||||||
|
return m_socket6.write(data, 17U, addr, addrLen);
|
||||||
|
default:
|
||||||
|
LogError("Unknown socket address family - %u", addr.ss_family);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNXDNNetwork::writeUnlink(const sockaddr_storage& addr, unsigned int addrLen, unsigned short tg)
|
bool CNXDNNetwork::writeUnlink(const sockaddr_storage& addr, unsigned int addrLen, unsigned short tg)
|
||||||
|
@ -140,7 +157,15 @@ bool CNXDNNetwork::writeUnlink(const sockaddr_storage& addr, unsigned int addrLe
|
||||||
if (m_debug)
|
if (m_debug)
|
||||||
CUtils::dump(1U, "NXDN Network Unlink Sent", data, 17U);
|
CUtils::dump(1U, "NXDN Network Unlink Sent", data, 17U);
|
||||||
|
|
||||||
return m_socket.write(data, 17U, addr, addrLen);
|
switch (addr.ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
return m_socket4.write(data, 17U, addr, addrLen);
|
||||||
|
case AF_INET6:
|
||||||
|
return m_socket6.write(data, 17U, addr, addrLen);
|
||||||
|
default:
|
||||||
|
LogError("Unknown socket address family - %u", addr.ss_family);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int CNXDNNetwork::readData(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen)
|
unsigned int CNXDNNetwork::readData(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen)
|
||||||
|
@ -148,7 +173,9 @@ unsigned int CNXDNNetwork::readData(unsigned char* data, unsigned int length, so
|
||||||
assert(data != NULL);
|
assert(data != NULL);
|
||||||
assert(length > 0U);
|
assert(length > 0U);
|
||||||
|
|
||||||
int len = m_socket.read(data, length, addr, addrLen);
|
int len = m_socket4.read(data, length, addr, addrLen);
|
||||||
|
if (len <= 0)
|
||||||
|
len = m_socket6.read(data, length, addr, addrLen);
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
|
@ -164,7 +191,8 @@ unsigned int CNXDNNetwork::readData(unsigned char* data, unsigned int length, so
|
||||||
|
|
||||||
void CNXDNNetwork::close()
|
void CNXDNNetwork::close()
|
||||||
{
|
{
|
||||||
m_socket.close();
|
m_socket4.close();
|
||||||
|
m_socket6.close();
|
||||||
|
|
||||||
LogInfo("Closing NXDN network connection");
|
LogInfo("Closing NXDN network connection");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2014,2016,2018,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -42,10 +42,10 @@ public:
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_callsign;
|
std::string m_callsign;
|
||||||
CUDPSocket m_socket;
|
CUDPSocket m_socket4;
|
||||||
unsigned short m_port;
|
CUDPSocket m_socket6;
|
||||||
bool m_debug;
|
bool m_debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2006-2016,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -34,29 +34,19 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) :
|
CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) :
|
||||||
m_address_save(address),
|
m_localAddress(address),
|
||||||
m_port_save(port),
|
m_localPort(port),
|
||||||
m_counter(0U)
|
m_fd(-1),
|
||||||
|
m_af(AF_UNSPEC)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(unsigned short port) :
|
CUDPSocket::CUDPSocket(unsigned short port) :
|
||||||
m_address_save(),
|
m_localAddress(),
|
||||||
m_port_save(port),
|
m_localPort(port),
|
||||||
m_counter(0U)
|
m_fd(-1),
|
||||||
|
m_af(AF_UNSPEC)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CUDPSocket::~CUDPSocket()
|
CUDPSocket::~CUDPSocket()
|
||||||
|
@ -93,10 +83,10 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
||||||
std::string portstr = std::to_string(port);
|
std::string portstr = std::to_string(port);
|
||||||
struct addrinfo *res;
|
struct addrinfo *res;
|
||||||
|
|
||||||
/* port is always digits, no needs to lookup service */
|
/* Port is always digits, no needs to lookup service */
|
||||||
hints.ai_flags |= AI_NUMERICSERV;
|
hints.ai_flags |= AI_NUMERICSERV;
|
||||||
|
|
||||||
int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
int err = ::getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
||||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
||||||
|
@ -109,7 +99,7 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
||||||
|
|
||||||
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
|
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
|
||||||
|
|
||||||
freeaddrinfo(res);
|
::freeaddrinfo(res);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -121,33 +111,33 @@ bool CUDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& ad
|
||||||
|
|
||||||
if (type == IMT_ADDRESS_AND_PORT) {
|
if (type == IMT_ADDRESS_AND_PORT) {
|
||||||
switch (addr1.ss_family) {
|
switch (addr1.ss_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
struct sockaddr_in *in_1, *in_2;
|
struct sockaddr_in *in_1, *in_2;
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
in_1 = (struct sockaddr_in*)&addr1;
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
in_2 = (struct sockaddr_in*)&addr2;
|
||||||
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
|
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
struct sockaddr_in6 *in6_1, *in6_2;
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
in6_1 = (struct sockaddr_in6*)&addr1;
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
in6_2 = (struct sockaddr_in6*)&addr2;
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
|
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (type == IMT_ADDRESS_ONLY) {
|
} else if (type == IMT_ADDRESS_ONLY) {
|
||||||
switch (addr1.ss_family) {
|
switch (addr1.ss_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
struct sockaddr_in *in_1, *in_2;
|
struct sockaddr_in *in_1, *in_2;
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
in_1 = (struct sockaddr_in*)&addr1;
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
in_2 = (struct sockaddr_in*)&addr2;
|
||||||
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
|
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
struct sockaddr_in6 *in6_1, *in6_2;
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
in6_1 = (struct sockaddr_in6*)&addr1;
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
in6_2 = (struct sockaddr_in6*)&addr2;
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
|
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -163,35 +153,34 @@ bool CUDPSocket::isNone(const sockaddr_storage& addr)
|
||||||
|
|
||||||
bool CUDPSocket::open(const sockaddr_storage& address)
|
bool CUDPSocket::open(const sockaddr_storage& address)
|
||||||
{
|
{
|
||||||
return open(address.ss_family);
|
m_af = address.ss_family;
|
||||||
|
|
||||||
|
return open();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUDPSocket::open(unsigned int af)
|
bool CUDPSocket::open()
|
||||||
{
|
{
|
||||||
return open(0, af, m_address_save, m_port_save);
|
assert(m_fd == -1);
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port)
|
|
||||||
{
|
|
||||||
sockaddr_storage addr;
|
sockaddr_storage addr;
|
||||||
unsigned int addrlen;
|
unsigned int addrlen;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
|
|
||||||
::memset(&hints, 0, sizeof(hints));
|
::memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
hints.ai_family = af;
|
hints.ai_family = m_af;
|
||||||
|
|
||||||
/* to determine protocol family, call lookup() first. */
|
// To determine protocol family, call lookup() on the local address first.
|
||||||
int err = lookup(address, port, addr, addrlen, hints);
|
int err = lookup(m_localAddress, m_localPort, addr, addrlen, hints);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
LogError("The local address is invalid - %s", address.c_str());
|
LogError("The local address is invalid - %s", m_localAddress.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(index);
|
m_af = addr.ss_family;
|
||||||
|
|
||||||
int fd = ::socket(addr.ss_family, SOCK_DGRAM, 0);
|
m_fd = ::socket(m_af, SOCK_DGRAM, 0);
|
||||||
if (fd < 0) {
|
if (m_fd < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
|
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
|
@ -200,62 +189,51 @@ bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_address[index] = address;
|
if (m_localPort > 0U) {
|
||||||
m_port[index] = port;
|
|
||||||
m_af[index] = addr.ss_family;
|
|
||||||
m_fd[index] = fd;
|
|
||||||
|
|
||||||
if (port > 0U) {
|
|
||||||
int reuse = 1;
|
int reuse = 1;
|
||||||
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
|
if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
|
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Cannot set the UDP socket option, err: %d", errno);
|
LogError("Cannot set the UDP socket option, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::bind(fd, (sockaddr*)&addr, addrlen) == -1) {
|
if (::bind(m_fd, (sockaddr*)&addr, addrlen) == -1) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
|
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Cannot bind the UDP address, err: %d", errno);
|
LogError("Cannot bind the UDP address, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo("Opening UDP port on %hu", port);
|
LogInfo("Opening UDP port on %hu", m_localPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length)
|
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &addressLength)
|
||||||
{
|
{
|
||||||
assert(buffer != NULL);
|
assert(buffer != NULL);
|
||||||
assert(length > 0U);
|
assert(length > 0U);
|
||||||
|
assert(m_fd >= 0);
|
||||||
|
|
||||||
// Check that the readfrom() won't block
|
// Check that the readfrom() won't block
|
||||||
int i, n;
|
struct pollfd pfd;
|
||||||
struct pollfd pfd[UDP_SOCKET_MAX];
|
pfd.fd = m_fd;
|
||||||
for (i = n = 0; i < UDP_SOCKET_MAX; i++) {
|
pfd.events = POLLIN;
|
||||||
if (m_fd[i] >= 0) {
|
pfd.revents = 0;
|
||||||
pfd[n].fd = m_fd[i];
|
|
||||||
pfd[n].events = POLLIN;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no socket descriptor to receive
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Return immediately
|
// Return immediately
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int ret = WSAPoll(pfd, n, 0);
|
int ret = WSAPoll(&pfd, 1, 0);
|
||||||
#else
|
#else
|
||||||
int ret = ::poll(pfd, n, 0);
|
int ret = ::poll(&pfd, 1, 0);
|
||||||
#endif
|
#endif
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -266,14 +244,7 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index;
|
if ((pfd.revents & POLLIN) == 0)
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
// round robin
|
|
||||||
index = (i + m_counter) % n;
|
|
||||||
if (pfd[index].revents & POLLIN)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == n)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -283,9 +254,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
||||||
#else
|
#else
|
||||||
ssize_t len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
||||||
#endif
|
#endif
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -294,7 +265,7 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
LogError("Error returned from recvfrom, err: %d", errno);
|
LogError("Error returned from recvfrom, err: %d", errno);
|
||||||
|
|
||||||
if (len == -1 && errno == ENOTSOCK) {
|
if (len == -1 && errno == ENOTSOCK) {
|
||||||
LogMessage("Re-opening UDP port on %hu", m_port[index]);
|
LogMessage("Re-opening UDP port on %hu", m_localPort);
|
||||||
close();
|
close();
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
@ -302,43 +273,39 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_counter++;
|
addressLength = size;
|
||||||
address_length = size;
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length)
|
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addressLength)
|
||||||
{
|
{
|
||||||
assert(buffer != NULL);
|
assert(buffer != NULL);
|
||||||
assert(length > 0U);
|
assert(length > 0U);
|
||||||
|
assert(m_fd >= 0);
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, addressLength);
|
||||||
#else
|
#else
|
||||||
ssize_t ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, addressLength);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Error returned from sendto, err: %lu", ::GetLastError());
|
LogError("Error returned from sendto, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Error returned from sendto, err: %d", errno);
|
LogError("Error returned from sendto, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
if (ret == int(length))
|
if (ret == int(length))
|
||||||
result = true;
|
result = true;
|
||||||
#else
|
#else
|
||||||
if (ret == ssize_t(length))
|
if (ret == ssize_t(length))
|
||||||
result = true;
|
result = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -346,18 +313,13 @@ bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const s
|
||||||
|
|
||||||
void CUDPSocket::close()
|
void CUDPSocket::close()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < UDP_SOCKET_MAX; i++)
|
if (m_fd >= 0) {
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::close(const unsigned int index)
|
|
||||||
{
|
|
||||||
if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
::closesocket(m_fd[index]);
|
::closesocket(m_fd);
|
||||||
#else
|
#else
|
||||||
::close(m_fd[index]);
|
::close(m_fd);
|
||||||
#endif
|
#endif
|
||||||
m_fd[index] = -1;
|
m_fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2011,2013,2015,2016,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -35,10 +35,6 @@
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(UDP_SOCKET_MAX)
|
|
||||||
#define UDP_SOCKET_MAX 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum IPMATCHTYPE {
|
enum IPMATCHTYPE {
|
||||||
IMT_ADDRESS_AND_PORT,
|
IMT_ADDRESS_AND_PORT,
|
||||||
IMT_ADDRESS_ONLY
|
IMT_ADDRESS_ONLY
|
||||||
|
@ -50,34 +46,33 @@ public:
|
||||||
CUDPSocket(unsigned short port = 0U);
|
CUDPSocket(unsigned short port = 0U);
|
||||||
~CUDPSocket();
|
~CUDPSocket();
|
||||||
|
|
||||||
bool open(unsigned int af = AF_UNSPEC);
|
bool open();
|
||||||
bool open(const sockaddr_storage& address);
|
bool open(const sockaddr_storage& address);
|
||||||
bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port);
|
|
||||||
|
|
||||||
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length);
|
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &addressLength);
|
||||||
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length);
|
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addressLength);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
void close(const unsigned int index);
|
|
||||||
|
|
||||||
static void startup();
|
static void startup();
|
||||||
static void shutdown();
|
static void shutdown();
|
||||||
|
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length);
|
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& addressLength);
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length, struct addrinfo& hints);
|
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& addressLength, struct addrinfo& hints);
|
||||||
|
|
||||||
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
|
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
|
||||||
|
|
||||||
static bool isNone(const sockaddr_storage& addr);
|
static bool isNone(const sockaddr_storage& addr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_address_save;
|
std::string m_localAddress;
|
||||||
unsigned short m_port_save;
|
unsigned short m_localPort;
|
||||||
std::string m_address[UDP_SOCKET_MAX];
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
unsigned short m_port[UDP_SOCKET_MAX];
|
SOCKET m_fd;
|
||||||
unsigned int m_af[UDP_SOCKET_MAX];
|
#else
|
||||||
int m_fd[UDP_SOCKET_MAX];
|
int m_fd;
|
||||||
unsigned int m_counter;
|
#endif
|
||||||
|
sa_family_t m_af;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015-2018,2020,2023,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,6 +19,6 @@
|
||||||
#if !defined(VERSION_H)
|
#if !defined(VERSION_H)
|
||||||
#define VERSION_H
|
#define VERSION_H
|
||||||
|
|
||||||
const char* VERSION = "20201105";
|
const char* VERSION = "20240129";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2006-2016,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -34,29 +34,19 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) :
|
CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) :
|
||||||
m_address_save(address),
|
m_localAddress(address),
|
||||||
m_port_save(port),
|
m_localPort(port),
|
||||||
m_counter(0U)
|
m_fd(-1),
|
||||||
|
m_af(AF_UNSPEC)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(unsigned short port) :
|
CUDPSocket::CUDPSocket(unsigned short port) :
|
||||||
m_address_save(),
|
m_localAddress(),
|
||||||
m_port_save(port),
|
m_localPort(port),
|
||||||
m_counter(0U)
|
m_fd(-1),
|
||||||
|
m_af(AF_UNSPEC)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CUDPSocket::~CUDPSocket()
|
CUDPSocket::~CUDPSocket()
|
||||||
|
@ -93,10 +83,10 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
||||||
std::string portstr = std::to_string(port);
|
std::string portstr = std::to_string(port);
|
||||||
struct addrinfo *res;
|
struct addrinfo *res;
|
||||||
|
|
||||||
/* port is always digits, no needs to lookup service */
|
/* Port is always digits, no needs to lookup service */
|
||||||
hints.ai_flags |= AI_NUMERICSERV;
|
hints.ai_flags |= AI_NUMERICSERV;
|
||||||
|
|
||||||
int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
int err = ::getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
||||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
||||||
|
@ -109,7 +99,7 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
||||||
|
|
||||||
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
|
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
|
||||||
|
|
||||||
freeaddrinfo(res);
|
::freeaddrinfo(res);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -121,33 +111,33 @@ bool CUDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& ad
|
||||||
|
|
||||||
if (type == IMT_ADDRESS_AND_PORT) {
|
if (type == IMT_ADDRESS_AND_PORT) {
|
||||||
switch (addr1.ss_family) {
|
switch (addr1.ss_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
struct sockaddr_in *in_1, *in_2;
|
struct sockaddr_in *in_1, *in_2;
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
in_1 = (struct sockaddr_in*)&addr1;
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
in_2 = (struct sockaddr_in*)&addr2;
|
||||||
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
|
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
struct sockaddr_in6 *in6_1, *in6_2;
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
in6_1 = (struct sockaddr_in6*)&addr1;
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
in6_2 = (struct sockaddr_in6*)&addr2;
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
|
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (type == IMT_ADDRESS_ONLY) {
|
} else if (type == IMT_ADDRESS_ONLY) {
|
||||||
switch (addr1.ss_family) {
|
switch (addr1.ss_family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
struct sockaddr_in *in_1, *in_2;
|
struct sockaddr_in *in_1, *in_2;
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
in_1 = (struct sockaddr_in*)&addr1;
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
in_2 = (struct sockaddr_in*)&addr2;
|
||||||
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
|
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
struct sockaddr_in6 *in6_1, *in6_2;
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
in6_1 = (struct sockaddr_in6*)&addr1;
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
in6_2 = (struct sockaddr_in6*)&addr2;
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
|
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -163,35 +153,34 @@ bool CUDPSocket::isNone(const sockaddr_storage& addr)
|
||||||
|
|
||||||
bool CUDPSocket::open(const sockaddr_storage& address)
|
bool CUDPSocket::open(const sockaddr_storage& address)
|
||||||
{
|
{
|
||||||
return open(address.ss_family);
|
m_af = address.ss_family;
|
||||||
|
|
||||||
|
return open();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUDPSocket::open(unsigned int af)
|
bool CUDPSocket::open()
|
||||||
{
|
{
|
||||||
return open(0, af, m_address_save, m_port_save);
|
assert(m_fd == -1);
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port)
|
|
||||||
{
|
|
||||||
sockaddr_storage addr;
|
sockaddr_storage addr;
|
||||||
unsigned int addrlen;
|
unsigned int addrlen;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
|
|
||||||
::memset(&hints, 0, sizeof(hints));
|
::memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
hints.ai_family = af;
|
hints.ai_family = m_af;
|
||||||
|
|
||||||
/* to determine protocol family, call lookup() first. */
|
// To determine protocol family, call lookup() on the local address first.
|
||||||
int err = lookup(address, port, addr, addrlen, hints);
|
int err = lookup(m_localAddress, m_localPort, addr, addrlen, hints);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
LogError("The local address is invalid - %s", address.c_str());
|
LogError("The local address is invalid - %s", m_localAddress.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(index);
|
m_af = addr.ss_family;
|
||||||
|
|
||||||
int fd = ::socket(addr.ss_family, SOCK_DGRAM, 0);
|
m_fd = ::socket(m_af, SOCK_DGRAM, 0);
|
||||||
if (fd < 0) {
|
if (m_fd < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
|
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
|
@ -200,62 +189,51 @@ bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_address[index] = address;
|
if (m_localPort > 0U) {
|
||||||
m_port[index] = port;
|
|
||||||
m_af[index] = addr.ss_family;
|
|
||||||
m_fd[index] = fd;
|
|
||||||
|
|
||||||
if (port > 0U) {
|
|
||||||
int reuse = 1;
|
int reuse = 1;
|
||||||
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
|
if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
|
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Cannot set the UDP socket option, err: %d", errno);
|
LogError("Cannot set the UDP socket option, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::bind(fd, (sockaddr*)&addr, addrlen) == -1) {
|
if (::bind(m_fd, (sockaddr*)&addr, addrlen) == -1) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
|
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Cannot bind the UDP address, err: %d", errno);
|
LogError("Cannot bind the UDP address, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo("Opening UDP port on %hu", port);
|
LogInfo("Opening UDP port on %hu", m_localPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length)
|
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &addressLength)
|
||||||
{
|
{
|
||||||
assert(buffer != NULL);
|
assert(buffer != NULL);
|
||||||
assert(length > 0U);
|
assert(length > 0U);
|
||||||
|
assert(m_fd >= 0);
|
||||||
|
|
||||||
// Check that the readfrom() won't block
|
// Check that the readfrom() won't block
|
||||||
int i, n;
|
struct pollfd pfd;
|
||||||
struct pollfd pfd[UDP_SOCKET_MAX];
|
pfd.fd = m_fd;
|
||||||
for (i = n = 0; i < UDP_SOCKET_MAX; i++) {
|
pfd.events = POLLIN;
|
||||||
if (m_fd[i] >= 0) {
|
pfd.revents = 0;
|
||||||
pfd[n].fd = m_fd[i];
|
|
||||||
pfd[n].events = POLLIN;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no socket descriptor to receive
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Return immediately
|
// Return immediately
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int ret = WSAPoll(pfd, n, 0);
|
int ret = WSAPoll(&pfd, 1, 0);
|
||||||
#else
|
#else
|
||||||
int ret = ::poll(pfd, n, 0);
|
int ret = ::poll(&pfd, 1, 0);
|
||||||
#endif
|
#endif
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -266,14 +244,7 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index;
|
if ((pfd.revents & POLLIN) == 0)
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
// round robin
|
|
||||||
index = (i + m_counter) % n;
|
|
||||||
if (pfd[index].revents & POLLIN)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == n)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -283,9 +254,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
||||||
#else
|
#else
|
||||||
ssize_t len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
||||||
#endif
|
#endif
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
@ -294,7 +265,7 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
LogError("Error returned from recvfrom, err: %d", errno);
|
LogError("Error returned from recvfrom, err: %d", errno);
|
||||||
|
|
||||||
if (len == -1 && errno == ENOTSOCK) {
|
if (len == -1 && errno == ENOTSOCK) {
|
||||||
LogMessage("Re-opening UDP port on %hu", m_port[index]);
|
LogMessage("Re-opening UDP port on %hu", m_localPort);
|
||||||
close();
|
close();
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
@ -302,43 +273,39 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_counter++;
|
addressLength = size;
|
||||||
address_length = size;
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length)
|
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addressLength)
|
||||||
{
|
{
|
||||||
assert(buffer != NULL);
|
assert(buffer != NULL);
|
||||||
assert(length > 0U);
|
assert(length > 0U);
|
||||||
|
assert(m_fd >= 0);
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
int ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, addressLength);
|
||||||
#else
|
#else
|
||||||
ssize_t ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, addressLength);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
LogError("Error returned from sendto, err: %lu", ::GetLastError());
|
LogError("Error returned from sendto, err: %lu", ::GetLastError());
|
||||||
#else
|
#else
|
||||||
LogError("Error returned from sendto, err: %d", errno);
|
LogError("Error returned from sendto, err: %d", errno);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
if (ret == int(length))
|
if (ret == int(length))
|
||||||
result = true;
|
result = true;
|
||||||
#else
|
#else
|
||||||
if (ret == ssize_t(length))
|
if (ret == ssize_t(length))
|
||||||
result = true;
|
result = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -346,18 +313,13 @@ bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const s
|
||||||
|
|
||||||
void CUDPSocket::close()
|
void CUDPSocket::close()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < UDP_SOCKET_MAX; i++)
|
if (m_fd >= 0) {
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::close(const unsigned int index)
|
|
||||||
{
|
|
||||||
if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
::closesocket(m_fd[index]);
|
::closesocket(m_fd);
|
||||||
#else
|
#else
|
||||||
::close(m_fd[index]);
|
::close(m_fd);
|
||||||
#endif
|
#endif
|
||||||
m_fd[index] = -1;
|
m_fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2011,2013,2015,2016,2020,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -35,10 +35,6 @@
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(UDP_SOCKET_MAX)
|
|
||||||
#define UDP_SOCKET_MAX 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum IPMATCHTYPE {
|
enum IPMATCHTYPE {
|
||||||
IMT_ADDRESS_AND_PORT,
|
IMT_ADDRESS_AND_PORT,
|
||||||
IMT_ADDRESS_ONLY
|
IMT_ADDRESS_ONLY
|
||||||
|
@ -50,34 +46,33 @@ public:
|
||||||
CUDPSocket(unsigned short port = 0U);
|
CUDPSocket(unsigned short port = 0U);
|
||||||
~CUDPSocket();
|
~CUDPSocket();
|
||||||
|
|
||||||
bool open(unsigned int af = AF_UNSPEC);
|
bool open();
|
||||||
bool open(const sockaddr_storage& address);
|
bool open(const sockaddr_storage& address);
|
||||||
bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port);
|
|
||||||
|
|
||||||
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length);
|
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &addressLength);
|
||||||
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length);
|
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addressLength);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
void close(const unsigned int index);
|
|
||||||
|
|
||||||
static void startup();
|
static void startup();
|
||||||
static void shutdown();
|
static void shutdown();
|
||||||
|
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length);
|
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& addressLength);
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length, struct addrinfo& hints);
|
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& addressLength, struct addrinfo& hints);
|
||||||
|
|
||||||
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
|
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
|
||||||
|
|
||||||
static bool isNone(const sockaddr_storage& addr);
|
static bool isNone(const sockaddr_storage& addr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_address_save;
|
std::string m_localAddress;
|
||||||
unsigned short m_port_save;
|
unsigned short m_localPort;
|
||||||
std::string m_address[UDP_SOCKET_MAX];
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
unsigned short m_port[UDP_SOCKET_MAX];
|
SOCKET m_fd;
|
||||||
unsigned int m_af[UDP_SOCKET_MAX];
|
#else
|
||||||
int m_fd[UDP_SOCKET_MAX];
|
int m_fd;
|
||||||
unsigned int m_counter;
|
#endif
|
||||||
|
sa_family_t m_af;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2018,2024 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,6 +19,6 @@
|
||||||
#if !defined(VERSION_H)
|
#if !defined(VERSION_H)
|
||||||
#define VERSION_H
|
#define VERSION_H
|
||||||
|
|
||||||
const char* VERSION = "20201101";
|
const char* VERSION = "20240129";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,287 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Conf.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
const int BUFFER_SIZE = 500;
|
|
||||||
|
|
||||||
enum SECTION {
|
|
||||||
SECTION_NONE,
|
|
||||||
SECTION_GENERAL,
|
|
||||||
SECTION_ID_LOOKUP,
|
|
||||||
SECTION_LOG,
|
|
||||||
SECTION_NETWORK,
|
|
||||||
SECTION_ICOM_NETWORK,
|
|
||||||
SECTION_KENWOOD_NETWORK
|
|
||||||
};
|
|
||||||
|
|
||||||
CConf::CConf(const std::string& file) :
|
|
||||||
m_file(file),
|
|
||||||
m_tg(9999U),
|
|
||||||
m_daemon(false),
|
|
||||||
m_lookupName(),
|
|
||||||
m_lookupTime(0U),
|
|
||||||
m_logDisplayLevel(0U),
|
|
||||||
m_logFileLevel(0U),
|
|
||||||
m_logFilePath(),
|
|
||||||
m_logFileRoot(),
|
|
||||||
m_logFileRotate(true),
|
|
||||||
m_networkPort(0U),
|
|
||||||
m_networkDebug(false),
|
|
||||||
m_icomEnabled(false),
|
|
||||||
m_icomAddress(),
|
|
||||||
m_icomTGEnable(0U),
|
|
||||||
m_icomTGDisable(0U),
|
|
||||||
m_icomDebug(false),
|
|
||||||
m_kenwoodEnabled(false),
|
|
||||||
m_kenwoodAddress(),
|
|
||||||
m_kenwoodTGEnable(0U),
|
|
||||||
m_kenwoodTGDisable(0U),
|
|
||||||
m_kenwoodDebug(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CConf::~CConf()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::read()
|
|
||||||
{
|
|
||||||
FILE* fp = ::fopen(m_file.c_str(), "rt");
|
|
||||||
if (fp == NULL) {
|
|
||||||
::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION section = SECTION_NONE;
|
|
||||||
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) {
|
|
||||||
if (buffer[0U] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (buffer[0U] == '[') {
|
|
||||||
if (::strncmp(buffer, "[General]", 9U) == 0)
|
|
||||||
section = SECTION_GENERAL;
|
|
||||||
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
|
|
||||||
section = SECTION_ID_LOOKUP;
|
|
||||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
|
||||||
section = SECTION_LOG;
|
|
||||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
|
||||||
section = SECTION_NETWORK;
|
|
||||||
else if (::strncmp(buffer, "[Icom Network]", 14U) == 0)
|
|
||||||
section = SECTION_ICOM_NETWORK;
|
|
||||||
else if (::strncmp(buffer, "[Kenwood Network]", 17U) == 0)
|
|
||||||
section = SECTION_KENWOOD_NETWORK;
|
|
||||||
else
|
|
||||||
section = SECTION_NONE;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* key = ::strtok(buffer, " \t=\r\n");
|
|
||||||
if (key == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char* value = ::strtok(NULL, "\r\n");
|
|
||||||
if (value == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Remove quotes from the value
|
|
||||||
size_t len = ::strlen(value);
|
|
||||||
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
|
|
||||||
value[len - 1U] = '\0';
|
|
||||||
value++;
|
|
||||||
} else {
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
// if value is not quoted, remove after # (to make comment)
|
|
||||||
if ((p = strchr(value, '#')) != NULL)
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
// remove trailing tab/space
|
|
||||||
for (p = value + strlen(value) - 1U; p >= value && (*p == '\t' || *p == ' '); p--)
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (section == SECTION_GENERAL) {
|
|
||||||
if (::strcmp(key, "Daemon") == 0)
|
|
||||||
m_daemon = ::atoi(value) == 1;
|
|
||||||
else if (::strcmp(key, "TG") == 0)
|
|
||||||
m_tg = (unsigned short)::atoi(value);
|
|
||||||
} else if (section == SECTION_ID_LOOKUP) {
|
|
||||||
if (::strcmp(key, "Name") == 0)
|
|
||||||
m_lookupName = value;
|
|
||||||
else if (::strcmp(key, "Time") == 0)
|
|
||||||
m_lookupTime = (unsigned int)::atoi(value);
|
|
||||||
} else if (section == SECTION_LOG) {
|
|
||||||
if (::strcmp(key, "FilePath") == 0)
|
|
||||||
m_logFilePath = value;
|
|
||||||
else if (::strcmp(key, "FileRoot") == 0)
|
|
||||||
m_logFileRoot = value;
|
|
||||||
else if (::strcmp(key, "FileLevel") == 0)
|
|
||||||
m_logFileLevel = (unsigned int)::atoi(value);
|
|
||||||
else if (::strcmp(key, "DisplayLevel") == 0)
|
|
||||||
m_logDisplayLevel = (unsigned int)::atoi(value);
|
|
||||||
else if (::strcmp(key, "FileRotate") == 0)
|
|
||||||
m_logFileRotate = ::atoi(value) == 1;
|
|
||||||
} else if (section == SECTION_NETWORK) {
|
|
||||||
if (::strcmp(key, "Port") == 0)
|
|
||||||
m_networkPort = (unsigned short)::atoi(value);
|
|
||||||
else if (::strcmp(key, "Debug") == 0)
|
|
||||||
m_networkDebug = ::atoi(value) == 1;
|
|
||||||
} else if (section == SECTION_ICOM_NETWORK) {
|
|
||||||
if (::strcmp(key, "Enabled") == 0)
|
|
||||||
m_icomEnabled = ::atoi(value) == 1;
|
|
||||||
else if (::strcmp(key, "Address") == 0)
|
|
||||||
m_icomAddress = value;
|
|
||||||
else if (::strcmp(key, "TGEnable") == 0)
|
|
||||||
m_icomTGEnable = (unsigned short)::atoi(value);
|
|
||||||
else if (::strcmp(key, "TGDisable") == 0)
|
|
||||||
m_icomTGDisable = (unsigned short)::atoi(value);
|
|
||||||
else if (::strcmp(key, "Debug") == 0)
|
|
||||||
m_icomDebug = ::atoi(value) == 1;
|
|
||||||
} else if (section == SECTION_KENWOOD_NETWORK) {
|
|
||||||
if (::strcmp(key, "Enabled") == 0)
|
|
||||||
m_kenwoodEnabled = ::atoi(value) == 1;
|
|
||||||
else if (::strcmp(key, "Address") == 0)
|
|
||||||
m_kenwoodAddress = value;
|
|
||||||
else if (::strcmp(key, "TGEnable") == 0)
|
|
||||||
m_kenwoodTGEnable = (unsigned short)::atoi(value);
|
|
||||||
else if (::strcmp(key, "TGDisable") == 0)
|
|
||||||
m_kenwoodTGDisable = (unsigned short)::atoi(value);
|
|
||||||
else if (::strcmp(key, "Debug") == 0)
|
|
||||||
m_kenwoodDebug = ::atoi(value) == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::fclose(fp);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getDaemon() const
|
|
||||||
{
|
|
||||||
return m_daemon;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getTG() const
|
|
||||||
{
|
|
||||||
return m_tg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CConf::getLookupName() const
|
|
||||||
{
|
|
||||||
return m_lookupName;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CConf::getLookupTime() const
|
|
||||||
{
|
|
||||||
return m_lookupTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CConf::getLogDisplayLevel() const
|
|
||||||
{
|
|
||||||
return m_logDisplayLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CConf::getLogFileLevel() const
|
|
||||||
{
|
|
||||||
return m_logFileLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CConf::getLogFilePath() const
|
|
||||||
{
|
|
||||||
return m_logFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CConf::getLogFileRoot() const
|
|
||||||
{
|
|
||||||
return m_logFileRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getLogFileRotate() const
|
|
||||||
{
|
|
||||||
return m_logFileRotate;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getNetworkPort() const
|
|
||||||
{
|
|
||||||
return m_networkPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getNetworkDebug() const
|
|
||||||
{
|
|
||||||
return m_networkDebug;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getIcomEnabled() const
|
|
||||||
{
|
|
||||||
return m_icomEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CConf::getIcomAddress() const
|
|
||||||
{
|
|
||||||
return m_icomAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getIcomTGEnable() const
|
|
||||||
{
|
|
||||||
return m_icomTGEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getIcomTGDisable() const
|
|
||||||
{
|
|
||||||
return m_icomTGDisable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getIcomDebug() const
|
|
||||||
{
|
|
||||||
return m_icomDebug;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getKenwoodEnabled() const
|
|
||||||
{
|
|
||||||
return m_kenwoodEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CConf::getKenwoodAddress() const
|
|
||||||
{
|
|
||||||
return m_kenwoodAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getKenwoodTGEnable() const
|
|
||||||
{
|
|
||||||
return m_kenwoodTGEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short CConf::getKenwoodTGDisable() const
|
|
||||||
{
|
|
||||||
return m_kenwoodTGDisable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CConf::getKenwoodDebug() const
|
|
||||||
{
|
|
||||||
return m_kenwoodDebug;
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(CONF_H)
|
|
||||||
#define CONF_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class CConf
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CConf(const std::string& file);
|
|
||||||
~CConf();
|
|
||||||
|
|
||||||
bool read();
|
|
||||||
|
|
||||||
// The General section
|
|
||||||
unsigned short getTG() const;
|
|
||||||
bool getDaemon() const;
|
|
||||||
|
|
||||||
// The Id Lookup section
|
|
||||||
std::string getLookupName() const;
|
|
||||||
unsigned int getLookupTime() const;
|
|
||||||
|
|
||||||
// The Log section
|
|
||||||
unsigned int getLogDisplayLevel() const;
|
|
||||||
unsigned int getLogFileLevel() const;
|
|
||||||
std::string getLogFilePath() const;
|
|
||||||
std::string getLogFileRoot() const;
|
|
||||||
bool getLogFileRotate() const;
|
|
||||||
|
|
||||||
// The Network section
|
|
||||||
unsigned short getNetworkPort() const;
|
|
||||||
bool getNetworkDebug() const;
|
|
||||||
|
|
||||||
// The Icom Network section
|
|
||||||
bool getIcomEnabled() const;
|
|
||||||
std::string getIcomAddress() const;
|
|
||||||
unsigned short getIcomTGEnable() const;
|
|
||||||
unsigned short getIcomTGDisable() const;
|
|
||||||
bool getIcomDebug() const;
|
|
||||||
|
|
||||||
// The Kenwood Network section
|
|
||||||
bool getKenwoodEnabled() const;
|
|
||||||
std::string getKenwoodAddress() const;
|
|
||||||
unsigned short getKenwoodTGEnable() const;
|
|
||||||
unsigned short getKenwoodTGDisable() const;
|
|
||||||
bool getKenwoodDebug() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_file;
|
|
||||||
unsigned short m_tg;
|
|
||||||
bool m_daemon;
|
|
||||||
|
|
||||||
std::string m_lookupName;
|
|
||||||
unsigned int m_lookupTime;
|
|
||||||
|
|
||||||
unsigned int m_logDisplayLevel;
|
|
||||||
unsigned int m_logFileLevel;
|
|
||||||
std::string m_logFilePath;
|
|
||||||
std::string m_logFileRoot;
|
|
||||||
bool m_logFileRotate;
|
|
||||||
|
|
||||||
unsigned short m_networkPort;
|
|
||||||
bool m_networkDebug;
|
|
||||||
|
|
||||||
bool m_icomEnabled;
|
|
||||||
std::string m_icomAddress;
|
|
||||||
unsigned short m_icomTGEnable;
|
|
||||||
unsigned short m_icomTGDisable;
|
|
||||||
bool m_icomDebug;
|
|
||||||
|
|
||||||
bool m_kenwoodEnabled;
|
|
||||||
std::string m_kenwoodAddress;
|
|
||||||
unsigned short m_kenwoodTGEnable;
|
|
||||||
unsigned short m_kenwoodTGDisable;
|
|
||||||
bool m_kenwoodDebug;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,139 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020,2021 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 "IcomNetwork.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
|
||||||
|
|
||||||
const unsigned int ICOM_PORT = 41300U;
|
|
||||||
|
|
||||||
CIcomNetwork::CIcomNetwork(const std::string& address, bool debug) :
|
|
||||||
m_socket(ICOM_PORT),
|
|
||||||
m_addr(),
|
|
||||||
m_addrLen(0U),
|
|
||||||
m_debug(debug)
|
|
||||||
{
|
|
||||||
assert(!address.empty());
|
|
||||||
|
|
||||||
if (CUDPSocket::lookup(address, ICOM_PORT, m_addr, m_addrLen) != 0)
|
|
||||||
m_addrLen = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
CIcomNetwork::~CIcomNetwork()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIcomNetwork::open()
|
|
||||||
{
|
|
||||||
if (m_addrLen == 0U) {
|
|
||||||
LogError("Unable to resolve the address of the Icom network");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = m_socket.open(m_addr);
|
|
||||||
if (!ret) {
|
|
||||||
LogError("Unable to open the Icom network connection");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage("Opened the Icom network connection");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIcomNetwork::write(const unsigned char* data, unsigned int len)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[110U];
|
|
||||||
::memset(buffer, 0x00U, 110U);
|
|
||||||
|
|
||||||
buffer[0U] = 'I';
|
|
||||||
buffer[1U] = 'C';
|
|
||||||
buffer[2U] = 'O';
|
|
||||||
buffer[3U] = 'M';
|
|
||||||
buffer[4U] = 0x01U;
|
|
||||||
buffer[5U] = 0x01U;
|
|
||||||
buffer[6U] = 0x08U;
|
|
||||||
buffer[7U] = 0xE0U;
|
|
||||||
|
|
||||||
if ((data[9U] & 0x02U) == 0x02U) {
|
|
||||||
buffer[37U] = 0x23U;
|
|
||||||
buffer[38U] = 0x02U;
|
|
||||||
buffer[39U] = 0x18U;
|
|
||||||
} else {
|
|
||||||
buffer[37U] = 0x23U;
|
|
||||||
buffer[38U] = (data[9U] & 0x0CU) != 0x00U ? 0x1CU : 0x10U;
|
|
||||||
buffer[39U] = 0x21U;
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(buffer + 40U, data + 10U, 33U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Icom Network Data Sent", buffer, 102U);
|
|
||||||
|
|
||||||
return m_socket.write(buffer, 102U, m_addr, m_addrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CIcomNetwork::read(unsigned char* data)
|
|
||||||
{
|
|
||||||
unsigned char buffer[BUFFER_LENGTH];
|
|
||||||
|
|
||||||
sockaddr_storage addr;
|
|
||||||
unsigned int addrLen;
|
|
||||||
int length = m_socket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
|
||||||
if (length <= 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
// Check if the data is for us
|
|
||||||
if (!CUDPSocket::match(m_addr, addr)) {
|
|
||||||
LogMessage("Icom packet received from an invalid source");
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid packet type?
|
|
||||||
if (::memcmp(buffer, "ICOM", 4U) != 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
if (length != 102)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Icom Network Data Received", buffer, length);
|
|
||||||
|
|
||||||
::memcpy(data, buffer + 40U, 33U);
|
|
||||||
|
|
||||||
return 33U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIcomNetwork::clock(unsigned int ms)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIcomNetwork::close()
|
|
||||||
{
|
|
||||||
m_socket.close();
|
|
||||||
|
|
||||||
LogMessage("Closing Icom network connection");
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IcomNetwork_H
|
|
||||||
#define IcomNetwork_H
|
|
||||||
|
|
||||||
#include "UDPSocket.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class CIcomNetwork {
|
|
||||||
public:
|
|
||||||
CIcomNetwork(const std::string& address, bool debug);
|
|
||||||
~CIcomNetwork();
|
|
||||||
|
|
||||||
bool open();
|
|
||||||
|
|
||||||
bool write(const unsigned char* data, unsigned int len);
|
|
||||||
|
|
||||||
unsigned int read(unsigned char* data);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void clock(unsigned int ms);
|
|
||||||
|
|
||||||
private:
|
|
||||||
CUDPSocket m_socket;
|
|
||||||
sockaddr_storage m_addr;
|
|
||||||
unsigned int m_addrLen;
|
|
||||||
bool m_debug;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,941 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020,2021 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 "KenwoodNetwork.h"
|
|
||||||
#include "NXDNCRC.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
|
||||||
|
|
||||||
#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])
|
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
|
||||||
|
|
||||||
const unsigned int RTP_PORT = 64000U;
|
|
||||||
const unsigned int RTCP_PORT = 64001U;
|
|
||||||
|
|
||||||
CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) :
|
|
||||||
m_rtpSocket(RTP_PORT),
|
|
||||||
m_rtcpSocket(RTCP_PORT),
|
|
||||||
m_rtpAddr(),
|
|
||||||
m_rtpAddrLen(0U),
|
|
||||||
m_rtcpAddr(),
|
|
||||||
m_rtcpAddrLen(0U),
|
|
||||||
m_headerSeen(false),
|
|
||||||
m_seen1(false),
|
|
||||||
m_seen2(false),
|
|
||||||
m_seen3(false),
|
|
||||||
m_seen4(false),
|
|
||||||
m_sacch(NULL),
|
|
||||||
m_sessionId(1U),
|
|
||||||
m_seqNo(0U),
|
|
||||||
m_ssrc(0U),
|
|
||||||
m_debug(debug),
|
|
||||||
m_startSecs(0U),
|
|
||||||
m_startUSecs(0U),
|
|
||||||
m_rtcpTimer(1000U, 0U, 200U),
|
|
||||||
m_hangTimer(1000U, 5U),
|
|
||||||
m_hangType(0U),
|
|
||||||
m_hangSrc(0U),
|
|
||||||
m_hangDst(0U),
|
|
||||||
m_random()
|
|
||||||
{
|
|
||||||
assert(!address.empty());
|
|
||||||
|
|
||||||
m_sacch = new unsigned char[10U];
|
|
||||||
|
|
||||||
if (CUDPSocket::lookup(address, RTP_PORT, m_rtpAddr, m_rtpAddrLen) != 0)
|
|
||||||
m_rtpAddrLen = 0U;
|
|
||||||
|
|
||||||
if (CUDPSocket::lookup(address, RTCP_PORT, m_rtcpAddr, m_rtcpAddrLen) != 0)
|
|
||||||
m_rtcpAddrLen = 0U;
|
|
||||||
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 mt(rd());
|
|
||||||
m_random = mt;
|
|
||||||
}
|
|
||||||
|
|
||||||
CKenwoodNetwork::~CKenwoodNetwork()
|
|
||||||
{
|
|
||||||
delete[] m_sacch;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::open()
|
|
||||||
{
|
|
||||||
if (m_rtpAddrLen == 0U || m_rtcpAddrLen == 0U) {
|
|
||||||
LogError("Unable to resolve the address of the Kenwood network");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_rtcpSocket.open(m_rtcpAddr)) {
|
|
||||||
LogError("Unable to open the Kenwood network connection");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_rtpSocket.open(m_rtpAddr)) {
|
|
||||||
LogError("Unable to open the Kenwood network connection");
|
|
||||||
m_rtcpSocket.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage("Opened the Kenwood network connection");
|
|
||||||
|
|
||||||
std::uniform_int_distribution<unsigned int> dist(0x00000001, 0xfffffffe);
|
|
||||||
m_ssrc = dist(m_random);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::write(const unsigned char* data, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
switch (data[0U]) {
|
|
||||||
case 0x81U: // Voice header or trailer
|
|
||||||
case 0x83U:
|
|
||||||
return processIcomVoiceHeader(data);
|
|
||||||
case 0xACU: // Voice data
|
|
||||||
case 0xAEU:
|
|
||||||
return processIcomVoiceData(data);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
|
|
||||||
{
|
|
||||||
assert(inData != NULL);
|
|
||||||
|
|
||||||
unsigned char outData[30U];
|
|
||||||
::memset(outData, 0x00U, 30U);
|
|
||||||
|
|
||||||
// SACCH
|
|
||||||
outData[0U] = inData[2U];
|
|
||||||
outData[1U] = inData[1U];
|
|
||||||
outData[2U] = inData[4U] & 0xC0U;
|
|
||||||
outData[3U] = inData[3U];
|
|
||||||
|
|
||||||
// FACCH 1+2
|
|
||||||
outData[4U] = outData[14U] = inData[6U];
|
|
||||||
outData[5U] = outData[15U] = inData[5U];
|
|
||||||
outData[6U] = outData[16U] = inData[8U];
|
|
||||||
outData[7U] = outData[17U] = inData[7U];
|
|
||||||
outData[8U] = outData[18U] = inData[10U];
|
|
||||||
outData[9U] = outData[19U] = inData[9U];
|
|
||||||
outData[10U] = outData[20U] = inData[12U];
|
|
||||||
outData[11U] = outData[21U] = inData[11U];
|
|
||||||
|
|
||||||
unsigned short src = (inData[8U] << 8) + (inData[9U] << 0);
|
|
||||||
unsigned short dst = (inData[10U] << 8) + (inData[11U] << 0);
|
|
||||||
unsigned char type = (inData[7U] >> 5) & 0x07U;
|
|
||||||
|
|
||||||
switch (inData[5U] & 0x3FU) {
|
|
||||||
case 0x01U:
|
|
||||||
m_hangTimer.stop();
|
|
||||||
m_rtcpTimer.start();
|
|
||||||
writeRTCPStart();
|
|
||||||
return writeRTPVoiceHeader(outData);
|
|
||||||
case 0x08U: {
|
|
||||||
m_hangTimer.start();
|
|
||||||
bool ret = writeRTPVoiceTrailer(outData);
|
|
||||||
writeRTCPHang(type, src, dst);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::processIcomVoiceData(const unsigned char* inData)
|
|
||||||
{
|
|
||||||
assert(inData != NULL);
|
|
||||||
|
|
||||||
unsigned char outData[40U], temp[10U];
|
|
||||||
::memset(outData, 0x00U, 40U);
|
|
||||||
|
|
||||||
// SACCH
|
|
||||||
outData[0U] = inData[2U];
|
|
||||||
outData[1U] = inData[1U];
|
|
||||||
outData[2U] = inData[4U] & 0xC0U;
|
|
||||||
outData[3U] = inData[3U];
|
|
||||||
|
|
||||||
// Audio 1
|
|
||||||
::memset(temp, 0x00U, 10U);
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
unsigned int offset = (5U * 8U) + i;
|
|
||||||
bool b = READ_BIT(inData, offset);
|
|
||||||
WRITE_BIT(temp, i, b);
|
|
||||||
}
|
|
||||||
outData[4U] = temp[1U];
|
|
||||||
outData[5U] = temp[0U];
|
|
||||||
outData[6U] = temp[3U];
|
|
||||||
outData[7U] = temp[2U];
|
|
||||||
outData[8U] = temp[5U];
|
|
||||||
outData[9U] = temp[4U];
|
|
||||||
outData[10U] = temp[7U];
|
|
||||||
outData[11U] = temp[6U];
|
|
||||||
|
|
||||||
// Audio 2
|
|
||||||
::memset(temp, 0x00U, 10U);
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
unsigned int offset = (5U * 8U) + 49U + i;
|
|
||||||
bool b = READ_BIT(inData, offset);
|
|
||||||
WRITE_BIT(temp, i, b);
|
|
||||||
}
|
|
||||||
outData[12U] = temp[1U];
|
|
||||||
outData[13U] = temp[0U];
|
|
||||||
outData[14U] = temp[3U];
|
|
||||||
outData[15U] = temp[2U];
|
|
||||||
outData[16U] = temp[5U];
|
|
||||||
outData[17U] = temp[4U];
|
|
||||||
outData[18U] = temp[7U];
|
|
||||||
outData[19U] = temp[6U];
|
|
||||||
|
|
||||||
// Audio 3
|
|
||||||
::memset(temp, 0x00U, 10U);
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
unsigned int offset = (19U * 8U) + i;
|
|
||||||
bool b = READ_BIT(inData, offset);
|
|
||||||
WRITE_BIT(temp, i, b);
|
|
||||||
}
|
|
||||||
outData[20U] = temp[1U];
|
|
||||||
outData[21U] = temp[0U];
|
|
||||||
outData[22U] = temp[3U];
|
|
||||||
outData[23U] = temp[2U];
|
|
||||||
outData[24U] = temp[5U];
|
|
||||||
outData[25U] = temp[4U];
|
|
||||||
outData[26U] = temp[7U];
|
|
||||||
outData[27U] = temp[6U];
|
|
||||||
|
|
||||||
// Audio 4
|
|
||||||
::memset(temp, 0x00U, 10U);
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
unsigned int offset = (19U * 8U) + 49U + i;
|
|
||||||
bool b = READ_BIT(inData, offset);
|
|
||||||
WRITE_BIT(temp, i, b);
|
|
||||||
}
|
|
||||||
outData[28U] = temp[1U];
|
|
||||||
outData[29U] = temp[0U];
|
|
||||||
outData[30U] = temp[3U];
|
|
||||||
outData[31U] = temp[2U];
|
|
||||||
outData[32U] = temp[5U];
|
|
||||||
outData[33U] = temp[4U];
|
|
||||||
outData[34U] = temp[7U];
|
|
||||||
outData[35U] = temp[6U];
|
|
||||||
|
|
||||||
return writeRTPVoiceData(outData);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[50U];
|
|
||||||
::memset(buffer, 0x00U, 50U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x80U;
|
|
||||||
buffer[1U] = 0x66U;
|
|
||||||
|
|
||||||
m_seqNo++;
|
|
||||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
||||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
unsigned long timeStamp = getTimeStamp();
|
|
||||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
m_sessionId++;
|
|
||||||
buffer[12U] = m_sessionId;
|
|
||||||
|
|
||||||
buffer[13U] = 0x00U;
|
|
||||||
buffer[14U] = 0x00U;
|
|
||||||
buffer[15U] = 0x00U;
|
|
||||||
buffer[16U] = 0x03U;
|
|
||||||
buffer[17U] = 0x03U;
|
|
||||||
buffer[18U] = 0x04U;
|
|
||||||
buffer[19U] = 0x04U;
|
|
||||||
buffer[20U] = 0x0AU;
|
|
||||||
buffer[21U] = 0x05U;
|
|
||||||
buffer[22U] = 0x0AU;
|
|
||||||
|
|
||||||
::memcpy(buffer + 23U, data, 24U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
|
|
||||||
|
|
||||||
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[50U];
|
|
||||||
::memset(buffer, 0x00U, 50U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x80U;
|
|
||||||
buffer[1U] = 0x66U;
|
|
||||||
|
|
||||||
m_seqNo++;
|
|
||||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
||||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
unsigned long timeStamp = getTimeStamp();
|
|
||||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[12U] = m_sessionId;
|
|
||||||
|
|
||||||
buffer[13U] = 0x00U;
|
|
||||||
buffer[14U] = 0x00U;
|
|
||||||
buffer[15U] = 0x00U;
|
|
||||||
buffer[16U] = 0x03U;
|
|
||||||
buffer[17U] = 0x03U;
|
|
||||||
buffer[18U] = 0x04U;
|
|
||||||
buffer[19U] = 0x04U;
|
|
||||||
buffer[20U] = 0x0AU;
|
|
||||||
buffer[21U] = 0x05U;
|
|
||||||
buffer[22U] = 0x0AU;
|
|
||||||
|
|
||||||
::memcpy(buffer + 23U, data, 24U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
|
|
||||||
|
|
||||||
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[60U];
|
|
||||||
::memset(buffer, 0x00U, 60U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x80U;
|
|
||||||
buffer[1U] = 0x66U;
|
|
||||||
|
|
||||||
m_seqNo++;
|
|
||||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
|
||||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
unsigned long timeStamp = getTimeStamp();
|
|
||||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[12U] = m_sessionId;
|
|
||||||
|
|
||||||
buffer[13U] = 0x00U;
|
|
||||||
buffer[14U] = 0x00U;
|
|
||||||
buffer[15U] = 0x00U;
|
|
||||||
buffer[16U] = 0x03U;
|
|
||||||
buffer[17U] = 0x02U;
|
|
||||||
buffer[18U] = 0x04U;
|
|
||||||
buffer[19U] = 0x07U;
|
|
||||||
buffer[20U] = 0x10U;
|
|
||||||
buffer[21U] = 0x08U;
|
|
||||||
buffer[22U] = 0x10U;
|
|
||||||
|
|
||||||
::memcpy(buffer + 23U, data, 36U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 59U);
|
|
||||||
|
|
||||||
return m_rtpSocket.write(buffer, 59U, m_rtpAddr, m_rtpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTCPStart()
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
time_t now;
|
|
||||||
::time(&now);
|
|
||||||
|
|
||||||
m_startSecs = uint32_t(now);
|
|
||||||
|
|
||||||
SYSTEMTIME st;
|
|
||||||
::GetSystemTime(&st);
|
|
||||||
|
|
||||||
m_startUSecs = st.wMilliseconds * 1000U;
|
|
||||||
#else
|
|
||||||
struct timeval tod;
|
|
||||||
::gettimeofday(&tod, NULL);
|
|
||||||
|
|
||||||
m_startSecs = tod.tv_sec;
|
|
||||||
m_startUSecs = tod.tv_usec;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned char buffer[30U];
|
|
||||||
::memset(buffer, 0x00U, 30U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x8AU;
|
|
||||||
buffer[1U] = 0xCCU;
|
|
||||||
buffer[2U] = 0x00U;
|
|
||||||
buffer[3U] = 0x06U;
|
|
||||||
|
|
||||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = 'K';
|
|
||||||
buffer[9U] = 'W';
|
|
||||||
buffer[10U] = 'N';
|
|
||||||
buffer[11U] = 'E';
|
|
||||||
|
|
||||||
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
|
||||||
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
|
||||||
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
|
||||||
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
|
||||||
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
|
||||||
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
|
||||||
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[22U] = 0x02U;
|
|
||||||
|
|
||||||
buffer[24U] = 0x01U;
|
|
||||||
|
|
||||||
buffer[27U] = 0x0AU;
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
|
||||||
|
|
||||||
return m_rtcpSocket.write(buffer, 28U, m_rtcpAddr, m_rtcpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTCPPing()
|
|
||||||
{
|
|
||||||
unsigned char buffer[30U];
|
|
||||||
::memset(buffer, 0x00U, 30U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x8AU;
|
|
||||||
buffer[1U] = 0xCCU;
|
|
||||||
buffer[2U] = 0x00U;
|
|
||||||
buffer[3U] = 0x06U;
|
|
||||||
|
|
||||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = 'K';
|
|
||||||
buffer[9U] = 'W';
|
|
||||||
buffer[10U] = 'N';
|
|
||||||
buffer[11U] = 'E';
|
|
||||||
|
|
||||||
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
|
||||||
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
|
||||||
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
|
||||||
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
|
||||||
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
|
||||||
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
|
||||||
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[22U] = 0x02U;
|
|
||||||
|
|
||||||
buffer[24U] = 0x01U;
|
|
||||||
|
|
||||||
buffer[27U] = 0x7BU;
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
|
||||||
|
|
||||||
return m_rtcpSocket.write(buffer, 28U, m_rtcpAddr, m_rtcpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
|
|
||||||
{
|
|
||||||
m_hangType = type;
|
|
||||||
m_hangSrc = src;
|
|
||||||
m_hangDst = dst;
|
|
||||||
|
|
||||||
return writeRTCPHang();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CKenwoodNetwork::writeRTCPHang()
|
|
||||||
{
|
|
||||||
unsigned char buffer[30U];
|
|
||||||
::memset(buffer, 0x00U, 30U);
|
|
||||||
|
|
||||||
buffer[0U] = 0x8BU;
|
|
||||||
buffer[1U] = 0xCCU;
|
|
||||||
buffer[2U] = 0x00U;
|
|
||||||
buffer[3U] = 0x04U;
|
|
||||||
|
|
||||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
|
||||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
|
||||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
|
||||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[8U] = 'K';
|
|
||||||
buffer[9U] = 'W';
|
|
||||||
buffer[10U] = 'N';
|
|
||||||
buffer[11U] = 'E';
|
|
||||||
|
|
||||||
buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
|
|
||||||
buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[14U] = (m_hangDst >> 8) & 0xFFU;
|
|
||||||
buffer[15U] = (m_hangDst >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[16U] = m_hangType;
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
|
|
||||||
|
|
||||||
return m_rtcpSocket.write(buffer, 20U, m_rtcpAddr, m_rtcpAddrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::read(unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char dummy[BUFFER_LENGTH];
|
|
||||||
readRTCP(dummy);
|
|
||||||
|
|
||||||
unsigned int len = readRTP(data);
|
|
||||||
switch (len) {
|
|
||||||
case 0U: // Nothing received
|
|
||||||
return 0U;
|
|
||||||
case 35U: // Voice header or trailer
|
|
||||||
return processKenwoodVoiceHeader(data);
|
|
||||||
case 47U: // Voice data
|
|
||||||
if (m_headerSeen)
|
|
||||||
return processKenwoodVoiceData(data);
|
|
||||||
else
|
|
||||||
return processKenwoodVoiceLateEntry(data);
|
|
||||||
case 31U: // Data
|
|
||||||
return processKenwoodData(data);
|
|
||||||
default:
|
|
||||||
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[BUFFER_LENGTH];
|
|
||||||
|
|
||||||
sockaddr_storage addr;
|
|
||||||
unsigned int addrLen;
|
|
||||||
int length = m_rtpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
|
||||||
if (length <= 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
// Check if the data is for us
|
|
||||||
if (!CUDPSocket::match(m_rtpAddr, addr, IMT_ADDRESS_ONLY)) {
|
|
||||||
LogMessage("Kenwood RTP packet received from an invalid source");
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
|
|
||||||
|
|
||||||
::memcpy(data, buffer + 12U, length - 12U);
|
|
||||||
|
|
||||||
return length - 12U;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
unsigned char buffer[BUFFER_LENGTH];
|
|
||||||
|
|
||||||
sockaddr_storage addr;
|
|
||||||
unsigned int addrLen;
|
|
||||||
int length = m_rtcpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
|
||||||
if (length <= 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
// Check if the data is for us
|
|
||||||
if (!CUDPSocket::match(m_rtcpAddr, addr, IMT_ADDRESS_ONLY)) {
|
|
||||||
LogMessage("Kenwood RTCP packet received from an invalid source");
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
|
|
||||||
|
|
||||||
if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
|
|
||||||
LogError("Missing RTCP KWNE signature");
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(data, buffer + 12U, length - 12U);
|
|
||||||
|
|
||||||
return length - 12U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKenwoodNetwork::close()
|
|
||||||
{
|
|
||||||
m_rtcpSocket.close();
|
|
||||||
m_rtpSocket.close();
|
|
||||||
|
|
||||||
LogMessage("Closing Kenwood connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKenwoodNetwork::clock(unsigned int ms)
|
|
||||||
{
|
|
||||||
m_rtcpTimer.clock(ms);
|
|
||||||
if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
|
|
||||||
if (m_hangTimer.isRunning())
|
|
||||||
writeRTCPHang();
|
|
||||||
else
|
|
||||||
writeRTCPPing();
|
|
||||||
m_rtcpTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hangTimer.clock(ms);
|
|
||||||
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
|
|
||||||
m_rtcpTimer.stop();
|
|
||||||
m_hangTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
|
|
||||||
{
|
|
||||||
assert(inData != NULL);
|
|
||||||
|
|
||||||
unsigned char outData[50U], temp[20U];
|
|
||||||
::memset(outData, 0x00U, 50U);
|
|
||||||
|
|
||||||
// LICH
|
|
||||||
outData[0U] = 0x83U;
|
|
||||||
|
|
||||||
// SACCH
|
|
||||||
::memset(temp, 0x00U, 20U);
|
|
||||||
temp[0U] = inData[12U];
|
|
||||||
temp[1U] = inData[11U];
|
|
||||||
temp[2U] = inData[14U];
|
|
||||||
temp[3U] = inData[13U];
|
|
||||||
CNXDNCRC::encodeCRC6(temp, 26U);
|
|
||||||
::memcpy(outData + 1U, temp, 4U);
|
|
||||||
|
|
||||||
// FACCH 1+2
|
|
||||||
::memset(temp, 0x00U, 20U);
|
|
||||||
temp[0U] = inData[16U];
|
|
||||||
temp[1U] = inData[15U];
|
|
||||||
temp[2U] = inData[18U];
|
|
||||||
temp[3U] = inData[17U];
|
|
||||||
temp[4U] = inData[20U];
|
|
||||||
temp[5U] = inData[19U];
|
|
||||||
temp[6U] = inData[22U];
|
|
||||||
temp[7U] = inData[21U];
|
|
||||||
temp[8U] = inData[24U];
|
|
||||||
temp[9U] = inData[23U];
|
|
||||||
CNXDNCRC::encodeCRC12(temp, 80U);
|
|
||||||
::memcpy(outData + 5U, temp, 12U);
|
|
||||||
::memcpy(outData + 19U, temp, 12U);
|
|
||||||
|
|
||||||
switch (outData[5U] & 0x3FU) {
|
|
||||||
case 0x01U:
|
|
||||||
::memcpy(inData, outData, 33U);
|
|
||||||
m_headerSeen = true;
|
|
||||||
m_seen1 = false;
|
|
||||||
m_seen2 = false;
|
|
||||||
m_seen3 = false;
|
|
||||||
m_seen4 = false;
|
|
||||||
return 33U;
|
|
||||||
case 0x08U:
|
|
||||||
::memcpy(inData, outData, 33U);
|
|
||||||
m_headerSeen = false;
|
|
||||||
m_seen1 = false;
|
|
||||||
m_seen2 = false;
|
|
||||||
m_seen3 = false;
|
|
||||||
m_seen4 = false;
|
|
||||||
return 33U;
|
|
||||||
default:
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
|
|
||||||
{
|
|
||||||
assert(inData != NULL);
|
|
||||||
|
|
||||||
unsigned char outData[50U], temp[20U];
|
|
||||||
::memset(outData, 0x00U, 50U);
|
|
||||||
|
|
||||||
// LICH
|
|
||||||
outData[0U] = 0xAEU;
|
|
||||||
|
|
||||||
// SACCH
|
|
||||||
::memset(temp, 0x00U, 20U);
|
|
||||||
temp[0U] = inData[12U];
|
|
||||||
temp[1U] = inData[11U];
|
|
||||||
temp[2U] = inData[14U];
|
|
||||||
temp[3U] = inData[13U];
|
|
||||||
CNXDNCRC::encodeCRC6(temp, 26U);
|
|
||||||
::memcpy(outData + 1U, temp, 4U);
|
|
||||||
|
|
||||||
// AMBE 1+2
|
|
||||||
unsigned int n = 5U * 8U;
|
|
||||||
|
|
||||||
temp[0U] = inData[16U];
|
|
||||||
temp[1U] = inData[15U];
|
|
||||||
temp[2U] = inData[18U];
|
|
||||||
temp[3U] = inData[17U];
|
|
||||||
temp[4U] = inData[20U];
|
|
||||||
temp[5U] = inData[19U];
|
|
||||||
temp[6U] = inData[22U];
|
|
||||||
temp[7U] = inData[21U];
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
|
||||||
bool b = READ_BIT(temp, i);
|
|
||||||
WRITE_BIT(outData, n, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
temp[0U] = inData[24U];
|
|
||||||
temp[1U] = inData[23U];
|
|
||||||
temp[2U] = inData[26U];
|
|
||||||
temp[3U] = inData[25U];
|
|
||||||
temp[4U] = inData[28U];
|
|
||||||
temp[5U] = inData[27U];
|
|
||||||
temp[6U] = inData[30U];
|
|
||||||
temp[7U] = inData[29U];
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
|
||||||
bool b = READ_BIT(temp, i);
|
|
||||||
WRITE_BIT(outData, n, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AMBE 3+4
|
|
||||||
n = 19U * 8U;
|
|
||||||
|
|
||||||
temp[0U] = inData[32U];
|
|
||||||
temp[1U] = inData[31U];
|
|
||||||
temp[2U] = inData[34U];
|
|
||||||
temp[3U] = inData[33U];
|
|
||||||
temp[4U] = inData[36U];
|
|
||||||
temp[5U] = inData[35U];
|
|
||||||
temp[6U] = inData[38U];
|
|
||||||
temp[7U] = inData[37U];
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
|
||||||
bool b = READ_BIT(temp, i);
|
|
||||||
WRITE_BIT(outData, n, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
temp[0U] = inData[40U];
|
|
||||||
temp[1U] = inData[39U];
|
|
||||||
temp[2U] = inData[42U];
|
|
||||||
temp[3U] = inData[41U];
|
|
||||||
temp[4U] = inData[44U];
|
|
||||||
temp[5U] = inData[43U];
|
|
||||||
temp[6U] = inData[46U];
|
|
||||||
temp[7U] = inData[45U];
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
|
||||||
bool b = READ_BIT(temp, i);
|
|
||||||
WRITE_BIT(outData, n, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(inData, outData, 33U);
|
|
||||||
|
|
||||||
return 33U;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::processKenwoodData(unsigned char* inData)
|
|
||||||
{
|
|
||||||
if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
unsigned char outData[50U];
|
|
||||||
|
|
||||||
if (inData[7U] == 0x09U || inData[7U] == 0x08U) {
|
|
||||||
outData[0U] = 0x90U;
|
|
||||||
outData[1U] = inData[8U];
|
|
||||||
outData[2U] = inData[7U];
|
|
||||||
outData[3U] = inData[10U];
|
|
||||||
outData[4U] = inData[9U];
|
|
||||||
outData[5U] = inData[12U];
|
|
||||||
outData[6U] = inData[11U];
|
|
||||||
::memcpy(inData, outData, 7U);
|
|
||||||
return 7U;
|
|
||||||
} else {
|
|
||||||
outData[0U] = 0x90U;
|
|
||||||
outData[1U] = inData[8U];
|
|
||||||
outData[2U] = inData[7U];
|
|
||||||
outData[3U] = inData[10U];
|
|
||||||
outData[4U] = inData[9U];
|
|
||||||
outData[5U] = inData[12U];
|
|
||||||
outData[6U] = inData[11U];
|
|
||||||
outData[7U] = inData[14U];
|
|
||||||
outData[8U] = inData[13U];
|
|
||||||
outData[9U] = inData[16U];
|
|
||||||
outData[10U] = inData[15U];
|
|
||||||
outData[11U] = inData[18U];
|
|
||||||
outData[12U] = inData[17U];
|
|
||||||
outData[13U] = inData[20U];
|
|
||||||
outData[14U] = inData[19U];
|
|
||||||
outData[15U] = inData[22U];
|
|
||||||
outData[16U] = inData[21U];
|
|
||||||
outData[17U] = inData[24U];
|
|
||||||
outData[18U] = inData[23U];
|
|
||||||
outData[19U] = inData[26U];
|
|
||||||
outData[20U] = inData[25U];
|
|
||||||
outData[21U] = inData[28U];
|
|
||||||
outData[22U] = inData[27U];
|
|
||||||
outData[23U] = inData[29U];
|
|
||||||
::memcpy(inData, outData, 24U);
|
|
||||||
return 24U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long CKenwoodNetwork::getTimeStamp() const
|
|
||||||
{
|
|
||||||
unsigned long timeStamp = 0UL;
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
SYSTEMTIME st;
|
|
||||||
::GetSystemTime(&st);
|
|
||||||
|
|
||||||
unsigned int hh = st.wHour;
|
|
||||||
unsigned int mm = st.wMinute;
|
|
||||||
unsigned int ss = st.wSecond;
|
|
||||||
unsigned int ms = st.wMilliseconds;
|
|
||||||
|
|
||||||
timeStamp += hh * 3600U * 1000U * 80U;
|
|
||||||
timeStamp += mm * 60U * 1000U * 80U;
|
|
||||||
timeStamp += ss * 1000U * 80U;
|
|
||||||
timeStamp += ms * 80U;
|
|
||||||
#else
|
|
||||||
struct timeval tod;
|
|
||||||
::gettimeofday(&tod, NULL);
|
|
||||||
|
|
||||||
unsigned int ss = tod.tv_sec;
|
|
||||||
unsigned int ms = tod.tv_usec / 1000U;
|
|
||||||
|
|
||||||
timeStamp += ss * 1000U * 80U;
|
|
||||||
timeStamp += ms * 80U;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
|
|
||||||
{
|
|
||||||
assert(inData != NULL);
|
|
||||||
|
|
||||||
unsigned char sacch[4U];
|
|
||||||
sacch[0U] = inData[12U];
|
|
||||||
sacch[1U] = inData[11U];
|
|
||||||
sacch[2U] = inData[14U];
|
|
||||||
sacch[3U] = inData[13U];
|
|
||||||
|
|
||||||
switch (sacch[0U] & 0xC0U) {
|
|
||||||
case 0xC0U:
|
|
||||||
if (!m_seen1) {
|
|
||||||
unsigned int offset = 0U;
|
|
||||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
||||||
bool b = READ_BIT(sacch, i) != 0U;
|
|
||||||
WRITE_BIT(m_sacch, offset, b);
|
|
||||||
}
|
|
||||||
m_seen1 = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x80U:
|
|
||||||
if (!m_seen2) {
|
|
||||||
unsigned int offset = 18U;
|
|
||||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
||||||
bool b = READ_BIT(sacch, i) != 0U;
|
|
||||||
WRITE_BIT(m_sacch, offset, b);
|
|
||||||
}
|
|
||||||
m_seen2 = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x40U:
|
|
||||||
if (!m_seen3) {
|
|
||||||
unsigned int offset = 36U;
|
|
||||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
||||||
bool b = READ_BIT(sacch, i) != 0U;
|
|
||||||
WRITE_BIT(m_sacch, offset, b);
|
|
||||||
}
|
|
||||||
m_seen3 = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x00U:
|
|
||||||
if (!m_seen4) {
|
|
||||||
unsigned int offset = 54U;
|
|
||||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
|
||||||
bool b = READ_BIT(sacch, i) != 0U;
|
|
||||||
WRITE_BIT(m_sacch, offset, b);
|
|
||||||
}
|
|
||||||
m_seen4 = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
// Create a dummy header
|
|
||||||
// Header SACCH
|
|
||||||
inData[11U] = 0x10U;
|
|
||||||
inData[12U] = 0x01U;
|
|
||||||
inData[13U] = 0x00U;
|
|
||||||
inData[14U] = 0x00U;
|
|
||||||
|
|
||||||
// Header FACCH
|
|
||||||
inData[15U] = m_sacch[1U];
|
|
||||||
inData[16U] = m_sacch[0U];
|
|
||||||
inData[17U] = m_sacch[3U];
|
|
||||||
inData[18U] = m_sacch[2U];
|
|
||||||
inData[19U] = m_sacch[5U];
|
|
||||||
inData[20U] = m_sacch[4U];
|
|
||||||
inData[21U] = m_sacch[7U];
|
|
||||||
inData[22U] = m_sacch[6U];
|
|
||||||
inData[23U] = 0x00U;
|
|
||||||
inData[24U] = m_sacch[8U];
|
|
||||||
|
|
||||||
return processKenwoodVoiceHeader(inData);
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KenwoodNetwork_H
|
|
||||||
#define KenwoodNetwork_H
|
|
||||||
|
|
||||||
#include "UDPSocket.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
class CKenwoodNetwork {
|
|
||||||
public:
|
|
||||||
CKenwoodNetwork(const std::string& address, bool debug);
|
|
||||||
~CKenwoodNetwork();
|
|
||||||
|
|
||||||
bool open();
|
|
||||||
|
|
||||||
bool write(const unsigned char* data, unsigned int length);
|
|
||||||
|
|
||||||
unsigned int read(unsigned char* data);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void clock(unsigned int ms);
|
|
||||||
|
|
||||||
private:
|
|
||||||
CUDPSocket m_rtpSocket;
|
|
||||||
CUDPSocket m_rtcpSocket;
|
|
||||||
sockaddr_storage m_rtpAddr;
|
|
||||||
unsigned int m_rtpAddrLen;
|
|
||||||
sockaddr_storage m_rtcpAddr;
|
|
||||||
unsigned int m_rtcpAddrLen;
|
|
||||||
bool m_headerSeen;
|
|
||||||
bool m_seen1;
|
|
||||||
bool m_seen2;
|
|
||||||
bool m_seen3;
|
|
||||||
bool m_seen4;
|
|
||||||
unsigned char* m_sacch;
|
|
||||||
uint8_t m_sessionId;
|
|
||||||
uint16_t m_seqNo;
|
|
||||||
unsigned int m_ssrc;
|
|
||||||
bool m_debug;
|
|
||||||
uint32_t m_startSecs;
|
|
||||||
uint32_t m_startUSecs;
|
|
||||||
CTimer m_rtcpTimer;
|
|
||||||
CTimer m_hangTimer;
|
|
||||||
unsigned char m_hangType;
|
|
||||||
unsigned short m_hangSrc;
|
|
||||||
unsigned short m_hangDst;
|
|
||||||
std::mt19937 m_random;
|
|
||||||
|
|
||||||
bool processIcomVoiceHeader(const unsigned char* data);
|
|
||||||
bool processIcomVoiceData(const unsigned char* data);
|
|
||||||
unsigned int processKenwoodVoiceHeader(unsigned char* data);
|
|
||||||
unsigned int processKenwoodVoiceData(unsigned char* data);
|
|
||||||
unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
|
|
||||||
unsigned int processKenwoodData(unsigned char* data);
|
|
||||||
bool writeRTPVoiceHeader(const unsigned char* data);
|
|
||||||
bool writeRTPVoiceData(const unsigned char* data);
|
|
||||||
bool writeRTPVoiceTrailer(const unsigned char* data);
|
|
||||||
bool writeRTCPStart();
|
|
||||||
bool writeRTCPPing();
|
|
||||||
bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
|
|
||||||
bool writeRTCPHang();
|
|
||||||
unsigned int readRTP(unsigned char* data);
|
|
||||||
unsigned int readRTCP(unsigned char* data);
|
|
||||||
unsigned long getTimeStamp() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,192 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
#include <Windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <ctime>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
static unsigned int m_fileLevel = 2U;
|
|
||||||
static std::string m_filePath;
|
|
||||||
static std::string m_fileRoot;
|
|
||||||
static bool m_fileRotate = true;
|
|
||||||
|
|
||||||
static FILE* m_fpLog = NULL;
|
|
||||||
static bool m_daemon = false;
|
|
||||||
|
|
||||||
static unsigned int m_displayLevel = 2U;
|
|
||||||
|
|
||||||
static struct tm m_tm;
|
|
||||||
|
|
||||||
static char LEVELS[] = " DMIWEF";
|
|
||||||
|
|
||||||
static bool logOpenRotate()
|
|
||||||
{
|
|
||||||
bool status = false;
|
|
||||||
|
|
||||||
if (m_fileLevel == 0U)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
time_t now;
|
|
||||||
::time(&now);
|
|
||||||
|
|
||||||
struct tm* tm = ::gmtime(&now);
|
|
||||||
|
|
||||||
if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) {
|
|
||||||
if (m_fpLog != NULL)
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (m_fpLog != NULL)
|
|
||||||
::fclose(m_fpLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
char filename[200U];
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
|
||||||
#else
|
|
||||||
::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((m_fpLog = ::fopen(filename, "a+t")) != NULL) {
|
|
||||||
status = true;
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
if (m_daemon)
|
|
||||||
dup2(fileno(m_fpLog), fileno(stderr));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tm = *tm;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool logOpenNoRotate()
|
|
||||||
{
|
|
||||||
bool status = false;
|
|
||||||
|
|
||||||
if (m_fileLevel == 0U)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (m_fpLog != NULL)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
char filename[200U];
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
::sprintf(filename, "%s\\%s.log", m_filePath.c_str(), m_fileRoot.c_str());
|
|
||||||
#else
|
|
||||||
::sprintf(filename, "%s/%s.log", m_filePath.c_str(), m_fileRoot.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((m_fpLog = ::fopen(filename, "a+t")) != NULL) {
|
|
||||||
status = true;
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
if (m_daemon)
|
|
||||||
dup2(fileno(m_fpLog), fileno(stderr));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LogOpen()
|
|
||||||
{
|
|
||||||
if (m_fileRotate)
|
|
||||||
return logOpenRotate();
|
|
||||||
else
|
|
||||||
return logOpenNoRotate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate)
|
|
||||||
{
|
|
||||||
m_filePath = filePath;
|
|
||||||
m_fileRoot = fileRoot;
|
|
||||||
m_fileLevel = fileLevel;
|
|
||||||
m_displayLevel = displayLevel;
|
|
||||||
m_daemon = daemon;
|
|
||||||
m_fileRotate = rotate;
|
|
||||||
|
|
||||||
if (m_daemon)
|
|
||||||
m_displayLevel = 0U;
|
|
||||||
|
|
||||||
return ::LogOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogFinalise()
|
|
||||||
{
|
|
||||||
if (m_fpLog != NULL)
|
|
||||||
::fclose(m_fpLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log(unsigned int level, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
assert(fmt != NULL);
|
|
||||||
|
|
||||||
char buffer[501U];
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
SYSTEMTIME st;
|
|
||||||
::GetSystemTime(&st);
|
|
||||||
|
|
||||||
::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
|
|
||||||
#else
|
|
||||||
struct timeval now;
|
|
||||||
::gettimeofday(&now, NULL);
|
|
||||||
|
|
||||||
struct tm* tm = ::gmtime(&now.tv_sec);
|
|
||||||
|
|
||||||
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lld ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000LL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
va_list vl;
|
|
||||||
va_start(vl, fmt);
|
|
||||||
|
|
||||||
::vsnprintf(buffer + ::strlen(buffer), 500, fmt, vl);
|
|
||||||
|
|
||||||
va_end(vl);
|
|
||||||
|
|
||||||
if (level >= m_fileLevel && m_fileLevel != 0U) {
|
|
||||||
bool ret = ::LogOpen();
|
|
||||||
if (!ret)
|
|
||||||
return;
|
|
||||||
|
|
||||||
::fprintf(m_fpLog, "%s\n", buffer);
|
|
||||||
::fflush(m_fpLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level >= m_displayLevel && m_displayLevel != 0U) {
|
|
||||||
::fprintf(stdout, "%s\n", buffer);
|
|
||||||
::fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level == 6U) { // Fatal
|
|
||||||
::fclose(m_fpLog);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(LOG_H)
|
|
||||||
#define LOG_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__)
|
|
||||||
#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__)
|
|
||||||
#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__)
|
|
||||||
#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__)
|
|
||||||
#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__)
|
|
||||||
#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
extern void Log(unsigned int level, const char* fmt, ...);
|
|
||||||
|
|
||||||
extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate);
|
|
||||||
extern void LogFinalise();
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,22 +0,0 @@
|
||||||
CC = cc
|
|
||||||
CXX = c++
|
|
||||||
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUDP_SOCKET_MAX=2 -std=c++0x -pthread
|
|
||||||
LIBS = -lpthread
|
|
||||||
LDFLAGS = -g
|
|
||||||
|
|
||||||
OBJECTS = Conf.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o NXDNCRC.o NXDNLookup.o NXDNNetwork.o NXDNReflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o
|
|
||||||
|
|
||||||
all: NXDNReflector
|
|
||||||
|
|
||||||
NXDNReflector: $(OBJECTS)
|
|
||||||
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o NXDNReflector
|
|
||||||
|
|
||||||
%.o: %.cpp
|
|
||||||
$(CXX) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
install:
|
|
||||||
install -m 755 NXDNReflector /usr/local/bin/
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) NXDNReflector *.o *.d *.bak *~
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,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 "Mutex.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
|
|
||||||
CMutex::CMutex() :
|
|
||||||
m_handle()
|
|
||||||
{
|
|
||||||
m_handle = ::CreateMutex(NULL, FALSE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
CMutex::~CMutex()
|
|
||||||
{
|
|
||||||
::CloseHandle(m_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMutex::lock()
|
|
||||||
{
|
|
||||||
::WaitForSingleObject(m_handle, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMutex::unlock()
|
|
||||||
{
|
|
||||||
::ReleaseMutex(m_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
CMutex::CMutex() :
|
|
||||||
m_mutex(PTHREAD_MUTEX_INITIALIZER)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CMutex::~CMutex()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMutex::lock()
|
|
||||||
{
|
|
||||||
::pthread_mutex_lock(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMutex::unlock()
|
|
||||||
{
|
|
||||||
::pthread_mutex_unlock(&m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,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(MUTEX_H)
|
|
||||||
#define MUTEX_H
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CMutex
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CMutex();
|
|
||||||
~CMutex();
|
|
||||||
|
|
||||||
void lock();
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
HANDLE m_handle;
|
|
||||||
#else
|
|
||||||
pthread_mutex_t m_mutex;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,185 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "NXDNCRC.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
|
||||||
|
|
||||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7])
|
|
||||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7])
|
|
||||||
|
|
||||||
bool CNXDNCRC::checkCRC6(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint8_t crc = createCRC6(in, length);
|
|
||||||
|
|
||||||
uint8_t temp[1U];
|
|
||||||
temp[0U] = 0x00U;
|
|
||||||
unsigned int j = length;
|
|
||||||
for (unsigned int i = 2U; i < 8U; i++, j++) {
|
|
||||||
bool b = READ_BIT1(in, j);
|
|
||||||
WRITE_BIT1(temp, i, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc == temp[0U];
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNCRC::encodeCRC6(unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint8_t crc[1U];
|
|
||||||
crc[0U] = createCRC6(in, length);
|
|
||||||
|
|
||||||
unsigned int n = length;
|
|
||||||
for (unsigned int i = 2U; i < 8U; i++, n++) {
|
|
||||||
bool b = READ_BIT1(crc, i);
|
|
||||||
WRITE_BIT1(in, n, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNCRC::checkCRC12(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint16_t crc = createCRC12(in, length);
|
|
||||||
uint8_t temp1[2U];
|
|
||||||
temp1[0U] = (crc >> 8) & 0xFFU;
|
|
||||||
temp1[1U] = (crc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
uint8_t temp2[2U];
|
|
||||||
temp2[0U] = 0x00U;
|
|
||||||
temp2[1U] = 0x00U;
|
|
||||||
unsigned int j = length;
|
|
||||||
for (unsigned int i = 4U; i < 16U; i++, j++) {
|
|
||||||
bool b = READ_BIT1(in, j);
|
|
||||||
WRITE_BIT1(temp2, i, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNCRC::encodeCRC12(unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint16_t crc = createCRC12(in, length);
|
|
||||||
|
|
||||||
uint8_t temp[2U];
|
|
||||||
temp[0U] = (crc >> 8) & 0xFFU;
|
|
||||||
temp[1U] = (crc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
unsigned int n = length;
|
|
||||||
for (unsigned int i = 4U; i < 16U; i++, n++) {
|
|
||||||
bool b = READ_BIT1(temp, i);
|
|
||||||
WRITE_BIT1(in, n, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNCRC::checkCRC15(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint16_t crc = createCRC15(in, length);
|
|
||||||
uint8_t temp1[2U];
|
|
||||||
temp1[0U] = (crc >> 8) & 0xFFU;
|
|
||||||
temp1[1U] = (crc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
uint8_t temp2[2U];
|
|
||||||
temp2[0U] = 0x00U;
|
|
||||||
temp2[1U] = 0x00U;
|
|
||||||
unsigned int j = length;
|
|
||||||
for (unsigned int i = 1U; i < 16U; i++, j++) {
|
|
||||||
bool b = READ_BIT1(in, j);
|
|
||||||
WRITE_BIT1(temp2, i, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNCRC::encodeCRC15(unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(in != NULL);
|
|
||||||
|
|
||||||
uint16_t crc = createCRC15(in, length);
|
|
||||||
|
|
||||||
uint8_t temp[2U];
|
|
||||||
temp[0U] = (crc >> 8) & 0xFFU;
|
|
||||||
temp[1U] = (crc >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
unsigned int n = length;
|
|
||||||
for (unsigned int i = 1U; i < 16U; i++, n++) {
|
|
||||||
bool b = READ_BIT1(temp, i);
|
|
||||||
WRITE_BIT1(in, n, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CNXDNCRC::createCRC6(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
uint8_t crc = 0x3FU;
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < length; i++) {
|
|
||||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
|
||||||
bool bit2 = (crc & 0x20U) == 0x20U;
|
|
||||||
|
|
||||||
crc <<= 1;
|
|
||||||
|
|
||||||
if (bit1 ^ bit2)
|
|
||||||
crc ^= 0x27U;
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc & 0x3FU;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CNXDNCRC::createCRC12(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
uint16_t crc = 0x0FFFU;
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < length; i++) {
|
|
||||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
|
||||||
bool bit2 = (crc & 0x0800U) == 0x0800U;
|
|
||||||
|
|
||||||
crc <<= 1;
|
|
||||||
|
|
||||||
if (bit1 ^ bit2)
|
|
||||||
crc ^= 0x080FU;
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc & 0x0FFFU;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CNXDNCRC::createCRC15(const unsigned char* in, unsigned int length)
|
|
||||||
{
|
|
||||||
uint16_t crc = 0x7FFFU;
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < length; i++) {
|
|
||||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
|
||||||
bool bit2 = (crc & 0x4000U) == 0x4000U;
|
|
||||||
|
|
||||||
crc <<= 1;
|
|
||||||
|
|
||||||
if (bit1 ^ bit2)
|
|
||||||
crc ^= 0x4CC5U;
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc & 0x7FFFU;
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(NXDNCRC_H)
|
|
||||||
#define NXDNCRC_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
class CNXDNCRC
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static bool checkCRC6(const unsigned char* in, unsigned int length);
|
|
||||||
static void encodeCRC6(unsigned char* in, unsigned int length);
|
|
||||||
|
|
||||||
static bool checkCRC12(const unsigned char* in, unsigned int length);
|
|
||||||
static void encodeCRC12(unsigned char* in, unsigned int length);
|
|
||||||
|
|
||||||
static bool checkCRC15(const unsigned char* in, unsigned int length);
|
|
||||||
static void encodeCRC15(unsigned char* in, unsigned int length);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static uint8_t createCRC6(const unsigned char* in, unsigned int length);
|
|
||||||
static uint16_t createCRC12(const unsigned char* in, unsigned int length);
|
|
||||||
static uint16_t createCRC15(const unsigned char* in, unsigned int length);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* 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 "NXDNLookup.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
CNXDNLookup::CNXDNLookup(const std::string& filename, unsigned int reloadTime) :
|
|
||||||
CThread(),
|
|
||||||
m_filename(filename),
|
|
||||||
m_reloadTime(reloadTime),
|
|
||||||
m_table(),
|
|
||||||
m_mutex(),
|
|
||||||
m_stop(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNLookup::~CNXDNLookup()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNLookup::read()
|
|
||||||
{
|
|
||||||
bool ret = load();
|
|
||||||
|
|
||||||
if (m_reloadTime > 0U)
|
|
||||||
run();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNLookup::entry()
|
|
||||||
{
|
|
||||||
LogInfo("Started the NXDN Id lookup reload thread");
|
|
||||||
|
|
||||||
CTimer timer(1U, 3600U * m_reloadTime);
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
while (!m_stop) {
|
|
||||||
sleep(1000U);
|
|
||||||
|
|
||||||
timer.clock();
|
|
||||||
if (timer.hasExpired()) {
|
|
||||||
load();
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogInfo("Stopped the NXDN Id lookup reload thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNLookup::stop()
|
|
||||||
{
|
|
||||||
if (m_reloadTime == 0U) {
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stop = true;
|
|
||||||
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CNXDNLookup::find(unsigned int id)
|
|
||||||
{
|
|
||||||
std::string callsign;
|
|
||||||
|
|
||||||
if (id == 0xFFFFU)
|
|
||||||
return std::string("ALL");
|
|
||||||
|
|
||||||
m_mutex.lock();
|
|
||||||
|
|
||||||
try {
|
|
||||||
callsign = m_table.at(id);
|
|
||||||
} catch (...) {
|
|
||||||
char text[10U];
|
|
||||||
::sprintf(text, "%u", id);
|
|
||||||
callsign = std::string(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mutex.unlock();
|
|
||||||
|
|
||||||
return callsign;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNLookup::exists(unsigned int id)
|
|
||||||
{
|
|
||||||
m_mutex.lock();
|
|
||||||
|
|
||||||
bool found = m_table.count(id) == 1U;
|
|
||||||
|
|
||||||
m_mutex.unlock();
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNLookup::load()
|
|
||||||
{
|
|
||||||
FILE* fp = ::fopen(m_filename.c_str(), "rt");
|
|
||||||
if (fp == NULL) {
|
|
||||||
LogWarning("Cannot open the NXDN Id lookup file - %s", m_filename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mutex.lock();
|
|
||||||
|
|
||||||
// Remove the old entries
|
|
||||||
m_table.clear();
|
|
||||||
|
|
||||||
char buffer[100U];
|
|
||||||
while (::fgets(buffer, 100U, 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) {
|
|
||||||
unsigned int id = (unsigned int)::atoi(p1);
|
|
||||||
if (id > 0U) {
|
|
||||||
for (char* p = p2; *p != 0x00U; p++)
|
|
||||||
*p = ::toupper(*p);
|
|
||||||
|
|
||||||
m_table[id] = std::string(p2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mutex.unlock();
|
|
||||||
|
|
||||||
::fclose(fp);
|
|
||||||
|
|
||||||
size_t size = m_table.size();
|
|
||||||
if (size == 0U)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
LogInfo("Loaded %u Ids to the NXDN callsign lookup table", size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef NXDNLookup_H
|
|
||||||
#define NXDNLookup_H
|
|
||||||
|
|
||||||
#include "Thread.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class CNXDNLookup : public CThread {
|
|
||||||
public:
|
|
||||||
CNXDNLookup(const std::string& filename, unsigned int reloadTime);
|
|
||||||
virtual ~CNXDNLookup();
|
|
||||||
|
|
||||||
bool read();
|
|
||||||
|
|
||||||
virtual void entry();
|
|
||||||
|
|
||||||
std::string find(unsigned int id);
|
|
||||||
|
|
||||||
bool exists(unsigned int id);
|
|
||||||
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_filename;
|
|
||||||
unsigned int m_reloadTime;
|
|
||||||
std::unordered_map<unsigned int, std::string> m_table;
|
|
||||||
CMutex m_mutex;
|
|
||||||
bool m_stop;
|
|
||||||
|
|
||||||
bool load();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "NXDNNetwork.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
CNXDNNetwork::CNXDNNetwork(unsigned short port, bool debug) :
|
|
||||||
m_socket(port),
|
|
||||||
m_debug(debug)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNNetwork::~CNXDNNetwork()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNNetwork::open()
|
|
||||||
{
|
|
||||||
LogInfo("Opening NXDN network connection");
|
|
||||||
|
|
||||||
return m_socket.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNNetwork::write(const unsigned char* data, unsigned int length, const sockaddr_storage& addr, unsigned int addrLen)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
assert(length > 0U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "NXDN Network Data Sent", data, length);
|
|
||||||
|
|
||||||
return m_socket.write(data, length, addr, addrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNNetwork::write(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
assert(length > 0U);
|
|
||||||
|
|
||||||
unsigned char buffer[50U];
|
|
||||||
|
|
||||||
buffer[0U] = 'N';
|
|
||||||
buffer[1U] = 'X';
|
|
||||||
buffer[2U] = 'D';
|
|
||||||
buffer[3U] = 'N';
|
|
||||||
buffer[4U] = 'D';
|
|
||||||
|
|
||||||
buffer[5U] = (srcId >> 8) & 0xFFU;
|
|
||||||
buffer[6U] = (srcId >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[7U] = (dstId >> 8) & 0xFFU;
|
|
||||||
buffer[8U] = (dstId >> 0) & 0xFFU;
|
|
||||||
|
|
||||||
buffer[9U] = 0x00U;
|
|
||||||
buffer[9U] |= grp ? 0x01U : 0x00U;
|
|
||||||
|
|
||||||
if (data[0U] == 0x81U || data[0U] == 0x83U) {
|
|
||||||
// This is a voice header or trailer.
|
|
||||||
buffer[9U] |= data[5U] == 0x01U ? 0x04U : 0x00U;
|
|
||||||
buffer[9U] |= data[5U] == 0x08U ? 0x08U : 0x00U;
|
|
||||||
} else if ((data[0U] & 0xF0U) == 0x90U) {
|
|
||||||
// This if data.
|
|
||||||
buffer[9U] |= 0x02U;
|
|
||||||
if (data[0U] == 0x90U || data[0U] == 0x92U || data[0U] == 0x9CU || data[0U] == 0x9EU) {
|
|
||||||
// This is data header or trailer.
|
|
||||||
buffer[9U] |= data[2U] == 0x09U ? 0x04U : 0x00U;
|
|
||||||
buffer[9U] |= data[2U] == 0x08U ? 0x08U : 0x00U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(buffer + 10U, data, 33U);
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "NXDN Network Data Sent", buffer, 43U);
|
|
||||||
|
|
||||||
return m_socket.write(buffer, 43U, addr, addrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CNXDNNetwork::read(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
assert(length > 0U);
|
|
||||||
|
|
||||||
int len = m_socket.read(data, length, addr, addrLen);
|
|
||||||
if (len <= 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
// Invalid packet type?
|
|
||||||
if (::memcmp(data, "NXDN", 4U) != 0)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
if (len != 17 && len != 43)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
if (m_debug)
|
|
||||||
CUtils::dump(1U, "NXDN Network Data Received", data, len);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNNetwork::close()
|
|
||||||
{
|
|
||||||
m_socket.close();
|
|
||||||
|
|
||||||
LogInfo("Closing NXDN network connection");
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef NXDNNetwork_H
|
|
||||||
#define NXDNNetwork_H
|
|
||||||
|
|
||||||
#include "UDPSocket.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class CNXDNNetwork {
|
|
||||||
public:
|
|
||||||
CNXDNNetwork(unsigned short port, bool debug);
|
|
||||||
~CNXDNNetwork();
|
|
||||||
|
|
||||||
bool open();
|
|
||||||
|
|
||||||
bool write(const unsigned char* data, unsigned int length, const sockaddr_storage& address, unsigned int addrLen);
|
|
||||||
bool write(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen);
|
|
||||||
|
|
||||||
unsigned int read(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CUDPSocket m_socket;
|
|
||||||
bool m_debug;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,629 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2016,2018,2020,2021 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 "NXDNReflector.h"
|
|
||||||
#include "NXDNNetwork.h"
|
|
||||||
#include "NXDNLookup.h"
|
|
||||||
#include "StopWatch.h"
|
|
||||||
#include "Version.h"
|
|
||||||
#include "Thread.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
#include <Windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
const char* DEFAULT_INI_FILE = "NXDNReflector.ini";
|
|
||||||
#else
|
|
||||||
const char* DEFAULT_INI_FILE = "/etc/NXDNReflector.ini";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <ctime>
|
|
||||||
#include <cstring>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
const char* iniFile = DEFAULT_INI_FILE;
|
|
||||||
if (argc > 1) {
|
|
||||||
for (int currentArg = 1; currentArg < argc; ++currentArg) {
|
|
||||||
std::string arg = argv[currentArg];
|
|
||||||
if ((arg == "-v") || (arg == "--version")) {
|
|
||||||
::fprintf(stdout, "NXDNReflector version %s\n", VERSION);
|
|
||||||
return 0;
|
|
||||||
} else if (arg.substr(0, 1) == "-") {
|
|
||||||
::fprintf(stderr, "Usage: NXDNReflector [-v|--version] [filename]\n");
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
iniFile = argv[currentArg];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNReflector* reflector = new CNXDNReflector(std::string(iniFile));
|
|
||||||
reflector->run();
|
|
||||||
delete reflector;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNReflector::CNXDNReflector(const std::string& file) :
|
|
||||||
m_conf(file),
|
|
||||||
m_icomNetwork(NULL),
|
|
||||||
m_kenwoodNetwork(NULL),
|
|
||||||
m_repeaters()
|
|
||||||
{
|
|
||||||
CUDPSocket::startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNReflector::~CNXDNReflector()
|
|
||||||
{
|
|
||||||
CUDPSocket::shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNReflector::run()
|
|
||||||
{
|
|
||||||
bool ret = m_conf.read();
|
|
||||||
if (!ret) {
|
|
||||||
::fprintf(stderr, "NXDNReflector: cannot read the .ini file\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
bool m_daemon = m_conf.getDaemon();
|
|
||||||
if (m_daemon) {
|
|
||||||
// Create new process
|
|
||||||
pid_t pid = ::fork();
|
|
||||||
if (pid == -1) {
|
|
||||||
::fprintf(stderr, "Couldn't fork() , exiting\n");
|
|
||||||
return;
|
|
||||||
} else if (pid != 0) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new session and process group
|
|
||||||
if (::setsid() == -1) {
|
|
||||||
::fprintf(stderr, "Couldn't setsid(), exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the working directory to the root directory
|
|
||||||
if (::chdir("/") == -1) {
|
|
||||||
::fprintf(stderr, "Couldn't cd /, exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are currently root...
|
|
||||||
if (getuid() == 0) {
|
|
||||||
struct passwd* user = ::getpwnam("mmdvm");
|
|
||||||
if (user == NULL) {
|
|
||||||
::fprintf(stderr, "Could not get the mmdvm user, exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uid_t mmdvm_uid = user->pw_uid;
|
|
||||||
gid_t mmdvm_gid = user->pw_gid;
|
|
||||||
|
|
||||||
// Set user and group ID's to mmdvm:mmdvm
|
|
||||||
if (setgid(mmdvm_gid) != 0) {
|
|
||||||
::fprintf(stderr, "Could not set mmdvm GID, exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setuid(mmdvm_uid) != 0) {
|
|
||||||
::fprintf(stderr, "Could not set mmdvm UID, exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double check it worked (AKA Paranoia)
|
|
||||||
if (setuid(0) != -1) {
|
|
||||||
::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
|
|
||||||
#else
|
|
||||||
ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
|
|
||||||
#endif
|
|
||||||
if (!ret) {
|
|
||||||
::fprintf(stderr, "NXDNReflector: unable to open the log file\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
if (m_daemon) {
|
|
||||||
::close(STDIN_FILENO);
|
|
||||||
::close(STDOUT_FILENO);
|
|
||||||
::close(STDERR_FILENO);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned short tg = m_conf.getTG();
|
|
||||||
|
|
||||||
CNXDNNetwork nxdnNetwork(m_conf.getNetworkPort(), m_conf.getNetworkDebug());
|
|
||||||
ret = nxdnNetwork.open();
|
|
||||||
if (!ret) {
|
|
||||||
::LogFinalise();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool icomEnabled = m_conf.getIcomEnabled();
|
|
||||||
|
|
||||||
unsigned short icomTGEnable = 0U;
|
|
||||||
unsigned short icomTGDisable = 0U;
|
|
||||||
|
|
||||||
if (icomEnabled) {
|
|
||||||
ret = openIcomNetwork();
|
|
||||||
if (!ret) {
|
|
||||||
nxdnNetwork.close();
|
|
||||||
::LogFinalise();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
icomTGEnable = m_conf.getIcomTGEnable();
|
|
||||||
icomTGDisable = m_conf.getIcomTGDisable();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kenwoodEnabled = m_conf.getKenwoodEnabled();
|
|
||||||
|
|
||||||
unsigned short kenwoodTGEnable = 0U;
|
|
||||||
unsigned short kenwoodTGDisable = 0U;
|
|
||||||
|
|
||||||
if (kenwoodEnabled) {
|
|
||||||
ret = openKenwoodNetwork();
|
|
||||||
if (!ret) {
|
|
||||||
nxdnNetwork.close();
|
|
||||||
::LogFinalise();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
kenwoodTGEnable = m_conf.getKenwoodTGEnable();
|
|
||||||
kenwoodTGDisable = m_conf.getKenwoodTGDisable();
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNLookup* lookup = new CNXDNLookup(m_conf.getLookupName(), m_conf.getLookupTime());
|
|
||||||
lookup->read();
|
|
||||||
|
|
||||||
CStopWatch stopWatch;
|
|
||||||
stopWatch.start();
|
|
||||||
|
|
||||||
CTimer dumpTimer(1000U, 120U);
|
|
||||||
dumpTimer.start();
|
|
||||||
|
|
||||||
LogMessage("Starting NXDNReflector-%s", VERSION);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ACTIVE_NONE,
|
|
||||||
ACTIVE_NXDN,
|
|
||||||
ACTIVE_ICOM,
|
|
||||||
ACTIVE_KENWOOD
|
|
||||||
} active = ACTIVE_NONE;
|
|
||||||
|
|
||||||
CNXDNRepeater* current = NULL;
|
|
||||||
|
|
||||||
unsigned short srcId = 0U;
|
|
||||||
unsigned short dstId = 0U;
|
|
||||||
bool grp = false;
|
|
||||||
|
|
||||||
CTimer watchdogTimer(1000U, 0U, 1500U);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
unsigned char buffer[200U];
|
|
||||||
sockaddr_storage address;
|
|
||||||
unsigned int addressLen;
|
|
||||||
|
|
||||||
unsigned int len = nxdnNetwork.read(buffer, 200U, address, addressLen);
|
|
||||||
if (len > 0U) {
|
|
||||||
CNXDNRepeater* rpt = findRepeater(address);
|
|
||||||
|
|
||||||
if (::memcmp(buffer, "NXDNP", 5U) == 0 && len == 17U) {
|
|
||||||
unsigned short id = (buffer[15U] << 8) | buffer[16U];
|
|
||||||
if (id == tg) {
|
|
||||||
if (rpt == NULL) {
|
|
||||||
rpt = new CNXDNRepeater;
|
|
||||||
rpt->m_timer.start();
|
|
||||||
::memcpy(&rpt->m_addr, &address, sizeof(struct sockaddr_storage));
|
|
||||||
rpt->m_addrLen = addressLen;
|
|
||||||
rpt->m_callsign = std::string((char*)(buffer + 5U), 10U);
|
|
||||||
m_repeaters.push_back(rpt);
|
|
||||||
|
|
||||||
char buff[80U];
|
|
||||||
LogMessage("Adding %s (%s)", rpt->m_callsign.c_str(), CUDPSocket::display(address, buff, 80U));
|
|
||||||
} else {
|
|
||||||
rpt->m_timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the poll
|
|
||||||
nxdnNetwork.write(buffer, len, address, addressLen);
|
|
||||||
}
|
|
||||||
} else if (::memcmp(buffer, "NXDNU", 5U) == 0 && len == 17U) {
|
|
||||||
unsigned short id = (buffer[15U] << 8) | buffer[16U];
|
|
||||||
if (id == tg) {
|
|
||||||
if (rpt != NULL) {
|
|
||||||
std::string callsign = std::string((char*)(buffer + 5U), 10U);
|
|
||||||
|
|
||||||
char buff[80U];
|
|
||||||
LogMessage("Removing %s (%s) unlinked", callsign.c_str(), CUDPSocket::display(address, buff, 80U));
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
|
||||||
if (*it == rpt) {
|
|
||||||
m_repeaters.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete rpt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (::memcmp(buffer, "NXDND", 5U) == 0 && len == 43U) {
|
|
||||||
if (rpt != NULL) {
|
|
||||||
unsigned short srcId = (buffer[5U] << 8) | buffer[6U];
|
|
||||||
unsigned short dstId = (buffer[7U] << 8) | buffer[8U];
|
|
||||||
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
|
||||||
|
|
||||||
if (icomEnabled && icomTGEnable != 0U && grp && dstId == icomTGEnable) {
|
|
||||||
if (m_icomNetwork == NULL) {
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Icom Network link enabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
|
||||||
bool ok = openIcomNetwork();
|
|
||||||
if (!ok)
|
|
||||||
LogWarning("Unable to open the Icom Network link");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kenwoodEnabled && kenwoodTGEnable != 0U && grp && dstId == kenwoodTGEnable) {
|
|
||||||
if (m_kenwoodNetwork == NULL) {
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Kenwood Network link enabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
|
||||||
bool ok = openKenwoodNetwork();
|
|
||||||
if (!ok)
|
|
||||||
LogWarning("Unable to open the Kenwood Network link");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icomEnabled && icomTGDisable != 0U && grp && dstId == icomTGDisable) {
|
|
||||||
if (m_icomNetwork != NULL) {
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Icom Network link disabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
|
||||||
closeIcomNetwork();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kenwoodEnabled && kenwoodTGDisable != 0U && grp && dstId == kenwoodTGDisable) {
|
|
||||||
if (m_kenwoodNetwork != NULL) {
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Kenwood Network link disabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
|
||||||
closeKenwoodNetwork();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grp && dstId == tg) {
|
|
||||||
rpt->m_timer.start();
|
|
||||||
|
|
||||||
if (current == NULL && active == ACTIVE_NONE) {
|
|
||||||
current = rpt;
|
|
||||||
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Transmission from %s at %s to %s%u", callsign.c_str(), current->m_callsign.c_str(), grp ? "TG " : "", dstId);
|
|
||||||
|
|
||||||
active = ACTIVE_NXDN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active == ACTIVE_NXDN) {
|
|
||||||
watchdogTimer.start();
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
|
||||||
if (!CUDPSocket::match((*it)->m_addr, address))
|
|
||||||
nxdnNetwork.write(buffer, len, (*it)->m_addr, (*it)->m_addrLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_icomNetwork != NULL)
|
|
||||||
m_icomNetwork->write(buffer, len);
|
|
||||||
|
|
||||||
if (m_kenwoodNetwork != NULL)
|
|
||||||
m_kenwoodNetwork->write(buffer, len);
|
|
||||||
|
|
||||||
if ((buffer[9U] & 0x08U) == 0x08U) {
|
|
||||||
LogMessage("Received end of transmission");
|
|
||||||
current = NULL;
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
watchdogTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogMessage("Data received from an unknown source");
|
|
||||||
CUtils::dump(2U, "Data", buffer, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_icomNetwork != NULL) {
|
|
||||||
len = m_icomNetwork->read(buffer);
|
|
||||||
if (len > 0U) {
|
|
||||||
if (current == NULL) {
|
|
||||||
if (active == ACTIVE_NONE) {
|
|
||||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x01U) {
|
|
||||||
bool tempGrp = (buffer[7U] & 0x20U) == 0x20U;
|
|
||||||
unsigned short tempSrcId = (buffer[8U] << 8) | buffer[9U];
|
|
||||||
unsigned short tempDstId = (buffer[10U] << 8) | buffer[11U];
|
|
||||||
|
|
||||||
if (tempGrp && tempDstId == tg) {
|
|
||||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
|
||||||
grp = tempGrp;
|
|
||||||
srcId = tempSrcId;
|
|
||||||
dstId = tempDstId;
|
|
||||||
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Transmission from %s on Icom Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
|
||||||
|
|
||||||
active = ACTIVE_ICOM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x09U) {
|
|
||||||
bool tempGrp = (buffer[4U] & 0x20U) == 0x20U;
|
|
||||||
unsigned short tempSrcId = (buffer[5U] << 8) | buffer[6U];
|
|
||||||
unsigned short tempDstId = (buffer[7U] << 8) | buffer[8U];
|
|
||||||
|
|
||||||
if (tempGrp && tempDstId == tg) {
|
|
||||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
|
||||||
grp = tempGrp;
|
|
||||||
srcId = tempSrcId;
|
|
||||||
dstId = tempDstId;
|
|
||||||
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Transmission from %s on Icom Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
|
||||||
|
|
||||||
active = ACTIVE_ICOM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active == ACTIVE_ICOM) {
|
|
||||||
watchdogTimer.start();
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
|
||||||
nxdnNetwork.write(buffer, len, srcId, dstId, grp, (*it)->m_addr, (*it)->m_addrLen);
|
|
||||||
|
|
||||||
if (m_kenwoodNetwork != NULL)
|
|
||||||
m_kenwoodNetwork->write(buffer, len);
|
|
||||||
|
|
||||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x08U) {
|
|
||||||
LogMessage("Received end of transmission");
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
watchdogTimer.stop();
|
|
||||||
}
|
|
||||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x08U) {
|
|
||||||
LogMessage("Received end of transmission");
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
watchdogTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_kenwoodNetwork != NULL) {
|
|
||||||
len = m_kenwoodNetwork->read(buffer);
|
|
||||||
if (len > 0U) {
|
|
||||||
if (current == NULL) {
|
|
||||||
if (active == ACTIVE_NONE) {
|
|
||||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x01U) {
|
|
||||||
bool tempGrp = (buffer[7U] & 0x20U) == 0x20U;
|
|
||||||
unsigned short tempSrcId = (buffer[8U] << 8) | buffer[9U];
|
|
||||||
unsigned short tempDstId = (buffer[10U] << 8) | buffer[11U];
|
|
||||||
|
|
||||||
if (tempGrp && tempDstId == tg) {
|
|
||||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
|
||||||
grp = tempGrp;
|
|
||||||
srcId = tempSrcId;
|
|
||||||
dstId = tempDstId;
|
|
||||||
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Transmission from %s on Kenwood Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
|
||||||
|
|
||||||
active = ACTIVE_KENWOOD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x09U) {
|
|
||||||
bool tempGrp = (buffer[4U] & 0x20U) == 0x20U;
|
|
||||||
unsigned short tempSrcId = (buffer[5U] << 8) | buffer[6U];
|
|
||||||
unsigned short tempDstId = (buffer[7U] << 8) | buffer[8U];
|
|
||||||
|
|
||||||
if (tempGrp && tempDstId == tg) {
|
|
||||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
|
||||||
grp = tempGrp;
|
|
||||||
srcId = tempSrcId;
|
|
||||||
dstId = tempDstId;
|
|
||||||
|
|
||||||
std::string callsign = lookup->find(srcId);
|
|
||||||
LogMessage("Transmission from %s on Kenwood Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
|
||||||
|
|
||||||
active = ACTIVE_KENWOOD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active == ACTIVE_KENWOOD) {
|
|
||||||
watchdogTimer.start();
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
|
||||||
nxdnNetwork.write(buffer, len, srcId, dstId, grp, (*it)->m_addr, (*it)->m_addrLen);
|
|
||||||
|
|
||||||
if (m_icomNetwork != NULL)
|
|
||||||
m_icomNetwork->write(buffer, len);
|
|
||||||
|
|
||||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x08U) {
|
|
||||||
LogMessage("Received end of transmission");
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
watchdogTimer.stop();
|
|
||||||
}
|
|
||||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x08U) {
|
|
||||||
LogMessage("Received end of transmission");
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
watchdogTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ms = stopWatch.elapsed();
|
|
||||||
stopWatch.start();
|
|
||||||
|
|
||||||
// Remove any repeaters that haven't reported for a while
|
|
||||||
for (std::vector<CNXDNRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
|
||||||
(*it)->m_timer.clock(ms);
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
|
||||||
if ((*it)->m_timer.hasExpired()) {
|
|
||||||
char buff[80U];
|
|
||||||
LogMessage("Removing %s (%s) disappeared", (*it)->m_callsign.c_str(),
|
|
||||||
CUDPSocket::display((*it)->m_addr, buff, 80U));
|
|
||||||
|
|
||||||
delete *it;
|
|
||||||
m_repeaters.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watchdogTimer.clock(ms);
|
|
||||||
if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) {
|
|
||||||
LogMessage("Network watchdog has expired");
|
|
||||||
watchdogTimer.stop();
|
|
||||||
current = NULL;
|
|
||||||
active = ACTIVE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
dumpTimer.clock(ms);
|
|
||||||
if (dumpTimer.hasExpired()) {
|
|
||||||
dumpRepeaters();
|
|
||||||
dumpTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_icomNetwork != NULL)
|
|
||||||
m_icomNetwork->clock(ms);
|
|
||||||
|
|
||||||
if (m_kenwoodNetwork != NULL)
|
|
||||||
m_kenwoodNetwork->clock(ms);
|
|
||||||
|
|
||||||
if (ms < 5U)
|
|
||||||
CThread::sleep(5U);
|
|
||||||
}
|
|
||||||
|
|
||||||
nxdnNetwork.close();
|
|
||||||
|
|
||||||
closeIcomNetwork();
|
|
||||||
|
|
||||||
closeKenwoodNetwork();
|
|
||||||
|
|
||||||
lookup->stop();
|
|
||||||
|
|
||||||
::LogFinalise();
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNRepeater* CNXDNReflector::findRepeater(const sockaddr_storage& addr) const
|
|
||||||
{
|
|
||||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
|
||||||
if (CUDPSocket::match(addr, (*it)->m_addr))
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNReflector::dumpRepeaters() const
|
|
||||||
{
|
|
||||||
if (m_repeaters.size() == 0U) {
|
|
||||||
LogMessage("No repeaters linked");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage("Currently linked repeaters:");
|
|
||||||
|
|
||||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
|
||||||
char buffer[80U];
|
|
||||||
LogMessage(" %s: %s %u/%u", (*it)->m_callsign.c_str(),
|
|
||||||
CUDPSocket::display((*it)->m_addr, buffer, 80U),
|
|
||||||
(*it)->m_timer.getTimer(),
|
|
||||||
(*it)->m_timer.getTimeout());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNReflector::openIcomNetwork()
|
|
||||||
{
|
|
||||||
m_icomNetwork = new CIcomNetwork(m_conf.getIcomAddress(), m_conf.getIcomDebug());
|
|
||||||
bool ret = m_icomNetwork->open();
|
|
||||||
if (!ret) {
|
|
||||||
delete m_icomNetwork;
|
|
||||||
m_icomNetwork = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CNXDNReflector::openKenwoodNetwork()
|
|
||||||
{
|
|
||||||
m_kenwoodNetwork = new CKenwoodNetwork(m_conf.getKenwoodAddress(), m_conf.getKenwoodDebug());
|
|
||||||
bool ret = m_kenwoodNetwork->open();
|
|
||||||
if (!ret) {
|
|
||||||
delete m_kenwoodNetwork;
|
|
||||||
m_kenwoodNetwork = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNReflector::closeIcomNetwork()
|
|
||||||
{
|
|
||||||
if (m_icomNetwork != NULL) {
|
|
||||||
m_icomNetwork->close();
|
|
||||||
delete m_icomNetwork;
|
|
||||||
m_icomNetwork = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNXDNReflector::closeKenwoodNetwork()
|
|
||||||
{
|
|
||||||
if (m_kenwoodNetwork != NULL) {
|
|
||||||
m_kenwoodNetwork->close();
|
|
||||||
delete m_kenwoodNetwork;
|
|
||||||
m_kenwoodNetwork = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2016,2018,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(NXDNReflector_H)
|
|
||||||
#define NXDNReflector_H
|
|
||||||
|
|
||||||
#include "KenwoodNetwork.h"
|
|
||||||
#include "IcomNetwork.h"
|
|
||||||
#include "Timer.h"
|
|
||||||
#include "Conf.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#else
|
|
||||||
#include <winsock.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CNXDNRepeater {
|
|
||||||
public:
|
|
||||||
CNXDNRepeater() :
|
|
||||||
m_addr(),
|
|
||||||
m_addrLen(0U),
|
|
||||||
m_callsign(),
|
|
||||||
m_timer(1000U, 120U)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
sockaddr_storage m_addr;
|
|
||||||
unsigned int m_addrLen;
|
|
||||||
std::string m_callsign;
|
|
||||||
CTimer m_timer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CNXDNReflector
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CNXDNReflector(const std::string& file);
|
|
||||||
~CNXDNReflector();
|
|
||||||
|
|
||||||
void run();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CConf m_conf;
|
|
||||||
CIcomNetwork* m_icomNetwork;
|
|
||||||
CKenwoodNetwork* m_kenwoodNetwork;
|
|
||||||
std::vector<CNXDNRepeater*> m_repeaters;
|
|
||||||
|
|
||||||
CNXDNRepeater* findRepeater(const sockaddr_storage& addr) const;
|
|
||||||
void dumpRepeaters() const;
|
|
||||||
|
|
||||||
bool openIcomNetwork();
|
|
||||||
bool openKenwoodNetwork();
|
|
||||||
void closeIcomNetwork();
|
|
||||||
void closeKenwoodNetwork();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,35 +0,0 @@
|
||||||
[General]
|
|
||||||
TG=9999
|
|
||||||
Daemon=1
|
|
||||||
|
|
||||||
[Id Lookup]
|
|
||||||
Name=NXDN.csv
|
|
||||||
Time=24
|
|
||||||
|
|
||||||
[Log]
|
|
||||||
# Logging levels, 0=No logging
|
|
||||||
DisplayLevel=1
|
|
||||||
FileLevel=1
|
|
||||||
FilePath=.
|
|
||||||
FileRoot=NXDNReflector
|
|
||||||
FileRotate=1
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
Port=41400
|
|
||||||
Debug=0
|
|
||||||
|
|
||||||
# Please visit www.nxdninfo.com if you are planning to link to the Icom NXCore server in Florida.
|
|
||||||
[Icom Network]
|
|
||||||
Enabled=0
|
|
||||||
Address=flicom.nxcore.org
|
|
||||||
# TGEnable=1234
|
|
||||||
# TGDisable=3456
|
|
||||||
Debug=0
|
|
||||||
|
|
||||||
# Note that the Kenwood NXCore server in Florida is offline.
|
|
||||||
[Kenwood Network]
|
|
||||||
Enabled=0
|
|
||||||
Address=flkenwood.nxcore.org
|
|
||||||
# TGEnable=1234
|
|
||||||
# TGDisable=3456
|
|
||||||
Debug=0
|
|
|
@ -1,76 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
#
|
|
||||||
# Provides: NXDNReflector
|
|
||||||
# Required-Start: $all
|
|
||||||
# Required-Stop:
|
|
||||||
# Default-Start: 2 3 4 5
|
|
||||||
# Default-Stop: 0 1 6
|
|
||||||
# Short-Description: Example startscript NXDNReflector
|
|
||||||
|
|
||||||
#
|
|
||||||
### END INIT INFO
|
|
||||||
## Fill in name of program here.
|
|
||||||
PROG="NXDNReflector"
|
|
||||||
PROG_PATH="/usr/local/bin/"
|
|
||||||
PROG_ARGS="/etc/NXDNReflector.ini"
|
|
||||||
PIDFILE="/var/run/NXDNReflector.pid"
|
|
||||||
USER="root"
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if [ -e $PIDFILE ]; then
|
|
||||||
## Program is running, exit with error.
|
|
||||||
echo "Error! $PROG is currently running!" 1>&2
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
cd $PROG_PATH
|
|
||||||
./$PROG $PROG_ARGS
|
|
||||||
echo "$PROG started"
|
|
||||||
touch $PIDFILE
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
if [ -e $PIDFILE ]; then
|
|
||||||
## Program is running, so stop it
|
|
||||||
echo "$PROG is running"
|
|
||||||
rm -f $PIDFILE
|
|
||||||
killall $PROG
|
|
||||||
echo "$PROG stopped"
|
|
||||||
else
|
|
||||||
## Program is not running, exit with error.
|
|
||||||
echo "Error! $PROG not started!" 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
## Check to see if we are running as root first.
|
|
||||||
## Found at
|
|
||||||
## http://www.cyberciti.biz/tips/shell-root-user-check-script.html
|
|
||||||
if [ "$(id -u)" != "0" ]; then
|
|
||||||
echo "This script must be run as root" 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
reload|restart|force-reload)
|
|
||||||
stop
|
|
||||||
sleep 5
|
|
||||||
start
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
**)
|
|
||||||
echo "Usage: $0 {start|stop|reload}" 1>&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
exit 0
|
|
||||||
### END
|
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.27130.2026
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NXDNReflector", "NXDNReflector.vcxproj", "{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.ActiveCfg = Debug|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Debug|x86.Build.0 = Debug|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x64.Build.0 = Release|x64
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.ActiveCfg = Release|Win32
|
|
||||||
{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}.Release|x86.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {32F19C39-C6E7-44AB-BE4C-EA8DD3C57231}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
|
@ -1,189 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="Conf.h" />
|
|
||||||
<ClInclude Include="IcomNetwork.h" />
|
|
||||||
<ClInclude Include="KenwoodNetwork.h" />
|
|
||||||
<ClInclude Include="NXDNCRC.h" />
|
|
||||||
<ClInclude Include="NXDNLookup.h" />
|
|
||||||
<ClInclude Include="Log.h" />
|
|
||||||
<ClInclude Include="Mutex.h" />
|
|
||||||
<ClInclude Include="NXDNNetwork.h" />
|
|
||||||
<ClInclude Include="NXDNReflector.h" />
|
|
||||||
<ClInclude Include="StopWatch.h" />
|
|
||||||
<ClInclude Include="Thread.h" />
|
|
||||||
<ClInclude Include="Timer.h" />
|
|
||||||
<ClInclude Include="UDPSocket.h" />
|
|
||||||
<ClInclude Include="Utils.h" />
|
|
||||||
<ClInclude Include="Version.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="Conf.cpp" />
|
|
||||||
<ClCompile Include="IcomNetwork.cpp" />
|
|
||||||
<ClCompile Include="KenwoodNetwork.cpp" />
|
|
||||||
<ClCompile Include="NXDNCRC.cpp" />
|
|
||||||
<ClCompile Include="NXDNLookup.cpp" />
|
|
||||||
<ClCompile Include="Log.cpp" />
|
|
||||||
<ClCompile Include="Mutex.cpp" />
|
|
||||||
<ClCompile Include="NXDNNetwork.cpp" />
|
|
||||||
<ClCompile Include="NXDNReflector.cpp" />
|
|
||||||
<ClCompile Include="StopWatch.cpp" />
|
|
||||||
<ClCompile Include="Thread.cpp" />
|
|
||||||
<ClCompile Include="Timer.cpp" />
|
|
||||||
<ClCompile Include="UDPSocket.cpp" />
|
|
||||||
<ClCompile Include="Utils.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
|
||||||
<ProjectGuid>{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}</ProjectGuid>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<RootNamespace>NXDNReflector</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>HAVE_LOG_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>HAVE_LOG_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
|
@ -1,104 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="Conf.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="NXDNLookup.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Log.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Mutex.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="NXDNReflector.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="StopWatch.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Thread.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Timer.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="UDPSocket.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Utils.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Version.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="NXDNNetwork.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="IcomNetwork.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="KenwoodNetwork.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="NXDNCRC.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="Conf.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="NXDNLookup.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Log.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Mutex.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="NXDNReflector.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="StopWatch.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Thread.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Timer.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="UDPSocket.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Utils.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="NXDNNetwork.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="IcomNetwork.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="KenwoodNetwork.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="NXDNCRC.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* 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 "StopWatch.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
|
|
||||||
CStopWatch::CStopWatch() :
|
|
||||||
m_frequencyS(),
|
|
||||||
m_frequencyMS(),
|
|
||||||
m_start()
|
|
||||||
{
|
|
||||||
::QueryPerformanceFrequency(&m_frequencyS);
|
|
||||||
|
|
||||||
m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CStopWatch::~CStopWatch()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long CStopWatch::time() const
|
|
||||||
{
|
|
||||||
LARGE_INTEGER now;
|
|
||||||
::QueryPerformanceCounter(&now);
|
|
||||||
|
|
||||||
return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long CStopWatch::start()
|
|
||||||
{
|
|
||||||
::QueryPerformanceCounter(&m_start);
|
|
||||||
|
|
||||||
return (unsigned long long)(m_start.QuadPart / m_frequencyS.QuadPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CStopWatch::elapsed()
|
|
||||||
{
|
|
||||||
LARGE_INTEGER now;
|
|
||||||
::QueryPerformanceCounter(&now);
|
|
||||||
|
|
||||||
LARGE_INTEGER temp;
|
|
||||||
temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000;
|
|
||||||
|
|
||||||
return (unsigned int)(temp.QuadPart / m_frequencyS.QuadPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
CStopWatch::CStopWatch() :
|
|
||||||
m_startMS(0ULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CStopWatch::~CStopWatch()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long CStopWatch::time() const
|
|
||||||
{
|
|
||||||
struct timeval now;
|
|
||||||
::gettimeofday(&now, NULL);
|
|
||||||
|
|
||||||
return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long CStopWatch::start()
|
|
||||||
{
|
|
||||||
struct timespec now;
|
|
||||||
::clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
m_startMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
|
|
||||||
|
|
||||||
return m_startMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CStopWatch::elapsed()
|
|
||||||
{
|
|
||||||
struct timespec now;
|
|
||||||
::clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
unsigned long long nowMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
|
|
||||||
|
|
||||||
return nowMS - m_startMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* 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(STOPWATCH_H)
|
|
||||||
#define STOPWATCH_H
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
#include <WS2tcpip.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CStopWatch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CStopWatch();
|
|
||||||
~CStopWatch();
|
|
||||||
|
|
||||||
unsigned long long time() const;
|
|
||||||
|
|
||||||
unsigned long long start();
|
|
||||||
unsigned int elapsed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LARGE_INTEGER m_frequencyS;
|
|
||||||
LARGE_INTEGER m_frequencyMS;
|
|
||||||
LARGE_INTEGER m_start;
|
|
||||||
#else
|
|
||||||
unsigned long long m_startMS;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Thread.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
|
|
||||||
CThread::CThread() :
|
|
||||||
m_handle()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CThread::~CThread()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CThread::run()
|
|
||||||
{
|
|
||||||
m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL);
|
|
||||||
|
|
||||||
return m_handle != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CThread::wait()
|
|
||||||
{
|
|
||||||
::WaitForSingleObject(m_handle, INFINITE);
|
|
||||||
|
|
||||||
::CloseHandle(m_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DWORD CThread::helper(LPVOID arg)
|
|
||||||
{
|
|
||||||
CThread* p = (CThread*)arg;
|
|
||||||
|
|
||||||
p->entry();
|
|
||||||
|
|
||||||
return 0UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CThread::sleep(unsigned int ms)
|
|
||||||
{
|
|
||||||
::Sleep(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
CThread::CThread() :
|
|
||||||
m_thread()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CThread::~CThread()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CThread::run()
|
|
||||||
{
|
|
||||||
return ::pthread_create(&m_thread, NULL, helper, this) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CThread::wait()
|
|
||||||
{
|
|
||||||
::pthread_join(m_thread, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void* CThread::helper(void* arg)
|
|
||||||
{
|
|
||||||
CThread* p = (CThread*)arg;
|
|
||||||
|
|
||||||
p->entry();
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CThread::sleep(unsigned int ms)
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
ts.tv_sec = ms / 1000U;
|
|
||||||
ts.tv_nsec = (ms % 1000U) * 1000000U;
|
|
||||||
|
|
||||||
::nanosleep(&ts, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,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(THREAD_H)
|
|
||||||
#define THREAD_H
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CThread();
|
|
||||||
virtual ~CThread();
|
|
||||||
|
|
||||||
virtual bool run();
|
|
||||||
|
|
||||||
virtual void entry() = 0;
|
|
||||||
|
|
||||||
virtual void wait();
|
|
||||||
|
|
||||||
static void sleep(unsigned int ms);
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
HANDLE m_handle;
|
|
||||||
#else
|
|
||||||
pthread_t m_thread;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
static DWORD __stdcall helper(LPVOID arg);
|
|
||||||
#else
|
|
||||||
static void* helper(void* arg);
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009,2010,2015 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 "Timer.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) :
|
|
||||||
m_ticksPerSec(ticksPerSec),
|
|
||||||
m_timeout(0U),
|
|
||||||
m_timer(0U)
|
|
||||||
{
|
|
||||||
assert(ticksPerSec > 0U);
|
|
||||||
|
|
||||||
if (secs > 0U || msecs > 0U) {
|
|
||||||
// m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U;
|
|
||||||
unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec;
|
|
||||||
m_timeout = (unsigned int)(temp / 1000ULL + 1ULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CTimer::~CTimer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTimer::setTimeout(unsigned int secs, unsigned int msecs)
|
|
||||||
{
|
|
||||||
if (secs > 0U || msecs > 0U) {
|
|
||||||
// m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U;
|
|
||||||
unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec;
|
|
||||||
m_timeout = (unsigned int)(temp / 1000ULL + 1ULL);
|
|
||||||
} else {
|
|
||||||
m_timeout = 0U;
|
|
||||||
m_timer = 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CTimer::getTimeout() const
|
|
||||||
{
|
|
||||||
if (m_timeout == 0U)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
return (m_timeout - 1U) / m_ticksPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CTimer::getTimer() const
|
|
||||||
{
|
|
||||||
if (m_timer == 0U)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
return (m_timer - 1U) / m_ticksPerSec;
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef Timer_H
|
|
||||||
#define Timer_H
|
|
||||||
|
|
||||||
class CTimer {
|
|
||||||
public:
|
|
||||||
CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U);
|
|
||||||
~CTimer();
|
|
||||||
|
|
||||||
void setTimeout(unsigned int secs, unsigned int msecs = 0U);
|
|
||||||
|
|
||||||
unsigned int getTimeout() const;
|
|
||||||
unsigned int getTimer() const;
|
|
||||||
|
|
||||||
unsigned int getRemaining()
|
|
||||||
{
|
|
||||||
if (m_timeout == 0U || m_timer == 0U)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
if (m_timer >= m_timeout)
|
|
||||||
return 0U;
|
|
||||||
|
|
||||||
return (m_timeout - m_timer) / m_ticksPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRunning()
|
|
||||||
{
|
|
||||||
return m_timer > 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void start(unsigned int secs, unsigned int msecs = 0U)
|
|
||||||
{
|
|
||||||
setTimeout(secs, msecs);
|
|
||||||
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
if (m_timeout > 0U)
|
|
||||||
m_timer = 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
m_timer = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasExpired()
|
|
||||||
{
|
|
||||||
if (m_timeout == 0U || m_timer == 0U)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_timer >= m_timeout)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clock(unsigned int ticks = 1U)
|
|
||||||
{
|
|
||||||
if (m_timer > 0U && m_timeout > 0U)
|
|
||||||
m_timer += ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned int m_ticksPerSec;
|
|
||||||
unsigned int m_timeout;
|
|
||||||
unsigned int m_timer;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,391 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2006-2016,2020,2021 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 "UDPSocket.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstring>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HAVE_LOG_H)
|
|
||||||
#include "Log.h"
|
|
||||||
#else
|
|
||||||
#define LogMessage(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
|
|
||||||
#define LogError(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
|
|
||||||
#define LogInfo(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) :
|
|
||||||
m_address_save(address),
|
|
||||||
m_port_save(port),
|
|
||||||
m_counter(0U)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CUDPSocket::CUDPSocket(unsigned short port) :
|
|
||||||
m_address_save(),
|
|
||||||
m_port_save(port),
|
|
||||||
m_counter(0U)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
m_address[i] = "";
|
|
||||||
m_port[i] = 0U;
|
|
||||||
m_af[i] = 0U;
|
|
||||||
m_fd[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CUDPSocket::~CUDPSocket()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::startup()
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
WSAData data;
|
|
||||||
int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data);
|
|
||||||
if (wsaRet != 0)
|
|
||||||
LogError("Error from WSAStartup");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::shutdown()
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
::WSACleanup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockaddr_storage& addr, unsigned int& address_length)
|
|
||||||
{
|
|
||||||
struct addrinfo hints;
|
|
||||||
::memset(&hints, 0, sizeof(hints));
|
|
||||||
|
|
||||||
return lookup(hostname, port, addr, address_length, hints);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockaddr_storage& addr, unsigned int& address_length, struct addrinfo& hints)
|
|
||||||
{
|
|
||||||
std::string portstr = std::to_string(port);
|
|
||||||
struct addrinfo *res;
|
|
||||||
|
|
||||||
/* port is always digits, no needs to lookup service */
|
|
||||||
hints.ai_flags |= AI_NUMERICSERV;
|
|
||||||
|
|
||||||
int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
|
||||||
if (err != 0) {
|
|
||||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
|
||||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
|
||||||
paddr->sin_family = AF_INET;
|
|
||||||
paddr->sin_port = htons(port);
|
|
||||||
paddr->sin_addr.s_addr = htonl(INADDR_NONE);
|
|
||||||
LogError("Cannot find address for host %s", hostname.c_str());
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
|
|
||||||
|
|
||||||
freeaddrinfo(res);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type)
|
|
||||||
{
|
|
||||||
if (addr1.ss_family != addr2.ss_family)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (type == IMT_ADDRESS_AND_PORT) {
|
|
||||||
switch (addr1.ss_family) {
|
|
||||||
case AF_INET:
|
|
||||||
struct sockaddr_in *in_1, *in_2;
|
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
|
||||||
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
|
|
||||||
case AF_INET6:
|
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (type == IMT_ADDRESS_ONLY) {
|
|
||||||
switch (addr1.ss_family) {
|
|
||||||
case AF_INET:
|
|
||||||
struct sockaddr_in *in_1, *in_2;
|
|
||||||
in_1 = (struct sockaddr_in*)&addr1;
|
|
||||||
in_2 = (struct sockaddr_in*)&addr2;
|
|
||||||
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
|
|
||||||
case AF_INET6:
|
|
||||||
struct sockaddr_in6 *in6_1, *in6_2;
|
|
||||||
in6_1 = (struct sockaddr_in6*)&addr1;
|
|
||||||
in6_2 = (struct sockaddr_in6*)&addr2;
|
|
||||||
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::isNone(const sockaddr_storage& addr)
|
|
||||||
{
|
|
||||||
struct sockaddr_in *in = (struct sockaddr_in *)&addr;
|
|
||||||
|
|
||||||
return ((addr.ss_family == AF_INET) && (in->sin_addr.s_addr == htonl(INADDR_NONE)));
|
|
||||||
}
|
|
||||||
|
|
||||||
char* CUDPSocket::display(const sockaddr_storage& addr, char* buffer, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(buffer != NULL);
|
|
||||||
assert(length > INET6_ADDRSTRLEN);
|
|
||||||
|
|
||||||
switch (addr.ss_family) {
|
|
||||||
case AF_INET: {
|
|
||||||
struct sockaddr_in* in4 = (struct sockaddr_in*)&addr;
|
|
||||||
::inet_ntop(AF_INET, &in4->sin_addr, buffer, length);
|
|
||||||
::sprintf(buffer + ::strlen(buffer), ":%u", in4->sin_port);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AF_INET6: {
|
|
||||||
struct sockaddr_in6* in6 = (struct sockaddr_in6*)&addr;
|
|
||||||
::inet_ntop(AF_INET6, &in6->sin6_addr, buffer, length);
|
|
||||||
::sprintf(buffer + ::strlen(buffer), ":%u", in6->sin6_port);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
::strcpy(buffer, "Unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::open(const sockaddr_storage& address)
|
|
||||||
{
|
|
||||||
return open(address.ss_family);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::open(unsigned int af)
|
|
||||||
{
|
|
||||||
return open(0, af, m_address_save, m_port_save);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port)
|
|
||||||
{
|
|
||||||
sockaddr_storage addr;
|
|
||||||
unsigned int addrlen;
|
|
||||||
struct addrinfo hints;
|
|
||||||
|
|
||||||
::memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_flags = AI_PASSIVE;
|
|
||||||
hints.ai_family = af;
|
|
||||||
|
|
||||||
/* to determine protocol family, call lookup() first. */
|
|
||||||
int err = lookup(address, port, addr, addrlen, hints);
|
|
||||||
if (err != 0) {
|
|
||||||
LogError("The local address is invalid - %s", address.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(index);
|
|
||||||
|
|
||||||
int fd = ::socket(addr.ss_family, SOCK_DGRAM, 0);
|
|
||||||
if (fd < 0) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Cannot create the UDP socket, err: %d", errno);
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_address[index] = address;
|
|
||||||
m_port[index] = port;
|
|
||||||
m_af[index] = addr.ss_family;
|
|
||||||
m_fd[index] = fd;
|
|
||||||
|
|
||||||
if (port > 0U) {
|
|
||||||
int reuse = 1;
|
|
||||||
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Cannot set the UDP socket option, err: %d", errno);
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (::bind(fd, (sockaddr*)&addr, addrlen) == -1) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Cannot bind the UDP address, err: %d", errno);
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogInfo("Opening UDP port on %hu", port);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length)
|
|
||||||
{
|
|
||||||
assert(buffer != NULL);
|
|
||||||
assert(length > 0U);
|
|
||||||
|
|
||||||
// Check that the readfrom() won't block
|
|
||||||
int i, n;
|
|
||||||
struct pollfd pfd[UDP_SOCKET_MAX];
|
|
||||||
for (i = n = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
if (m_fd[i] >= 0) {
|
|
||||||
pfd[n].fd = m_fd[i];
|
|
||||||
pfd[n].events = POLLIN;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no socket descriptor to receive
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Return immediately
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
int ret = WSAPoll(pfd, n, 0);
|
|
||||||
#else
|
|
||||||
int ret = ::poll(pfd, n, 0);
|
|
||||||
#endif
|
|
||||||
if (ret < 0) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Error returned from UDP poll, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Error returned from UDP poll, err: %d", errno);
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index;
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
// round robin
|
|
||||||
index = (i + m_counter) % n;
|
|
||||||
if (pfd[index].revents & POLLIN)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == n)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
int size = sizeof(sockaddr_storage);
|
|
||||||
#else
|
|
||||||
socklen_t size = sizeof(sockaddr_storage);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
int len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
|
||||||
#else
|
|
||||||
ssize_t len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
|
|
||||||
#endif
|
|
||||||
if (len <= 0) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Error returned from recvfrom, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Error returned from recvfrom, err: %d", errno);
|
|
||||||
|
|
||||||
if (len == -1 && errno == ENOTSOCK) {
|
|
||||||
LogMessage("Re-opening UDP port on %hu", m_port[index]);
|
|
||||||
close();
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_counter++;
|
|
||||||
address_length = size;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length)
|
|
||||||
{
|
|
||||||
assert(buffer != NULL);
|
|
||||||
assert(length > 0U);
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
|
|
||||||
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
int ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
|
||||||
#else
|
|
||||||
ssize_t ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
LogError("Error returned from sendto, err: %lu", ::GetLastError());
|
|
||||||
#else
|
|
||||||
LogError("Error returned from sendto, err: %d", errno);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
if (ret == int(length))
|
|
||||||
result = true;
|
|
||||||
#else
|
|
||||||
if (ret == ssize_t(length))
|
|
||||||
result = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::close()
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < UDP_SOCKET_MAX; i++)
|
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUDPSocket::close(const unsigned int index)
|
|
||||||
{
|
|
||||||
if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) {
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
::closesocket(m_fd[index]);
|
|
||||||
#else
|
|
||||||
::close(m_fd[index]);
|
|
||||||
#endif
|
|
||||||
m_fd[index] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef UDPSocket_H
|
|
||||||
#define UDPSocket_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(_WIN64)
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#else
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(UDP_SOCKET_MAX)
|
|
||||||
#define UDP_SOCKET_MAX 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum IPMATCHTYPE {
|
|
||||||
IMT_ADDRESS_AND_PORT,
|
|
||||||
IMT_ADDRESS_ONLY
|
|
||||||
};
|
|
||||||
|
|
||||||
class CUDPSocket {
|
|
||||||
public:
|
|
||||||
CUDPSocket(const std::string& address, unsigned short port = 0U);
|
|
||||||
CUDPSocket(unsigned short port = 0U);
|
|
||||||
~CUDPSocket();
|
|
||||||
|
|
||||||
bool open(unsigned int af = AF_UNSPEC);
|
|
||||||
bool open(const sockaddr_storage& address);
|
|
||||||
bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned short port);
|
|
||||||
|
|
||||||
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length);
|
|
||||||
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
void close(const unsigned int index);
|
|
||||||
|
|
||||||
static void startup();
|
|
||||||
static void shutdown();
|
|
||||||
|
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length);
|
|
||||||
static int lookup(const std::string& hostName, unsigned short port, sockaddr_storage& address, unsigned int& address_length, struct addrinfo& hints);
|
|
||||||
|
|
||||||
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
|
|
||||||
|
|
||||||
static bool isNone(const sockaddr_storage& addr);
|
|
||||||
|
|
||||||
static char* display(const sockaddr_storage& address, char* buffer, unsigned int length);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_address_save;
|
|
||||||
unsigned short m_port_save;
|
|
||||||
std::string m_address[UDP_SOCKET_MAX];
|
|
||||||
unsigned short m_port[UDP_SOCKET_MAX];
|
|
||||||
unsigned int m_af[UDP_SOCKET_MAX];
|
|
||||||
int m_fd[UDP_SOCKET_MAX];
|
|
||||||
unsigned int m_counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,146 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
dump(2U, title, data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
::Log(level, "%s", title.c_str());
|
|
||||||
|
|
||||||
unsigned int offset = 0U;
|
|
||||||
|
|
||||||
while (length > 0U) {
|
|
||||||
std::string output;
|
|
||||||
|
|
||||||
unsigned int bytes = (length > 16U) ? 16U : length;
|
|
||||||
|
|
||||||
for (unsigned i = 0U; i < bytes; i++) {
|
|
||||||
char temp[10U];
|
|
||||||
::sprintf(temp, "%02X ", data[offset + i]);
|
|
||||||
output += temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = bytes; i < 16U; i++)
|
|
||||||
output += " ";
|
|
||||||
|
|
||||||
output += " *";
|
|
||||||
|
|
||||||
for (unsigned i = 0U; i < bytes; i++) {
|
|
||||||
unsigned char c = data[offset + i];
|
|
||||||
|
|
||||||
if (::isprint(c))
|
|
||||||
output += c;
|
|
||||||
else
|
|
||||||
output += '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
output += '*';
|
|
||||||
|
|
||||||
::Log(level, "%04X: %s", offset, output.c_str());
|
|
||||||
|
|
||||||
offset += 16U;
|
|
||||||
|
|
||||||
if (length >= 16U)
|
|
||||||
length -= 16U;
|
|
||||||
else
|
|
||||||
length = 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::dump(const std::string& title, const bool* bits, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
dump(2U, title, bits, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
unsigned char bytes[100U];
|
|
||||||
unsigned int nBytes = 0U;
|
|
||||||
for (unsigned int n = 0U; n < length; n += 8U, nBytes++)
|
|
||||||
bitsToByteBE(bits + n, bytes[nBytes]);
|
|
||||||
|
|
||||||
dump(level, title, bytes, nBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::byteToBitsBE(unsigned char byte, bool* bits)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
bits[0U] = (byte & 0x80U) == 0x80U;
|
|
||||||
bits[1U] = (byte & 0x40U) == 0x40U;
|
|
||||||
bits[2U] = (byte & 0x20U) == 0x20U;
|
|
||||||
bits[3U] = (byte & 0x10U) == 0x10U;
|
|
||||||
bits[4U] = (byte & 0x08U) == 0x08U;
|
|
||||||
bits[5U] = (byte & 0x04U) == 0x04U;
|
|
||||||
bits[6U] = (byte & 0x02U) == 0x02U;
|
|
||||||
bits[7U] = (byte & 0x01U) == 0x01U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::byteToBitsLE(unsigned char byte, bool* bits)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
bits[0U] = (byte & 0x01U) == 0x01U;
|
|
||||||
bits[1U] = (byte & 0x02U) == 0x02U;
|
|
||||||
bits[2U] = (byte & 0x04U) == 0x04U;
|
|
||||||
bits[3U] = (byte & 0x08U) == 0x08U;
|
|
||||||
bits[4U] = (byte & 0x10U) == 0x10U;
|
|
||||||
bits[5U] = (byte & 0x20U) == 0x20U;
|
|
||||||
bits[6U] = (byte & 0x40U) == 0x40U;
|
|
||||||
bits[7U] = (byte & 0x80U) == 0x80U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
byte = bits[0U] ? 0x80U : 0x00U;
|
|
||||||
byte |= bits[1U] ? 0x40U : 0x00U;
|
|
||||||
byte |= bits[2U] ? 0x20U : 0x00U;
|
|
||||||
byte |= bits[3U] ? 0x10U : 0x00U;
|
|
||||||
byte |= bits[4U] ? 0x08U : 0x00U;
|
|
||||||
byte |= bits[5U] ? 0x04U : 0x00U;
|
|
||||||
byte |= bits[6U] ? 0x02U : 0x00U;
|
|
||||||
byte |= bits[7U] ? 0x01U : 0x00U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte)
|
|
||||||
{
|
|
||||||
assert(bits != NULL);
|
|
||||||
|
|
||||||
byte = bits[0U] ? 0x01U : 0x00U;
|
|
||||||
byte |= bits[1U] ? 0x02U : 0x00U;
|
|
||||||
byte |= bits[2U] ? 0x04U : 0x00U;
|
|
||||||
byte |= bits[3U] ? 0x08U : 0x00U;
|
|
||||||
byte |= bits[4U] ? 0x10U : 0x00U;
|
|
||||||
byte |= bits[5U] ? 0x20U : 0x00U;
|
|
||||||
byte |= bits[6U] ? 0x40U : 0x00U;
|
|
||||||
byte |= bits[7U] ? 0x80U : 0x00U;
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009,2014,2015 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; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef Utils_H
|
|
||||||
#define Utils_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class CUtils {
|
|
||||||
public:
|
|
||||||
static void dump(const std::string& title, const unsigned char* data, unsigned int length);
|
|
||||||
static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length);
|
|
||||||
|
|
||||||
static void dump(const std::string& title, const bool* bits, unsigned int length);
|
|
||||||
static void dump(int level, const std::string& title, const bool* bits, unsigned int length);
|
|
||||||
|
|
||||||
static void byteToBitsBE(unsigned char byte, bool* bits);
|
|
||||||
static void byteToBitsLE(unsigned char byte, bool* bits);
|
|
||||||
|
|
||||||
static void bitsToByteBE(const bool* bits, unsigned char& byte);
|
|
||||||
static void bitsToByteLE(const bool* bits, unsigned char& byte);
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015,2016,2018,2020,2021 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(VERSION_H)
|
|
||||||
#define VERSION_H
|
|
||||||
|
|
||||||
const char* VERSION = "20210912";
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -3,11 +3,6 @@ These programs are clients for the NXDN networking built into the MMDVM Host.
|
||||||
The Parrot is very simple minded and can only handle one client at a time and
|
The Parrot is very simple minded and can only handle one client at a time and
|
||||||
is therefore not suitable for use as a shared resource via the Internet.
|
is therefore not suitable for use as a shared resource via the Internet.
|
||||||
|
|
||||||
The Reflector is used as a single talk group in the same way that it is with
|
|
||||||
P25. It also includes the option to link it to NXCore to allow for interchange
|
|
||||||
of audio between the two. At the NXCore end, it should be set up to receive the
|
|
||||||
traffic from only one talk group.
|
|
||||||
|
|
||||||
The Gateway allows for use of NXDN Talk Groups to control the access to the
|
The Gateway allows for use of NXDN Talk Groups to control the access to the
|
||||||
various NXDN reflectors. It speaks the same language as Icom repeaters to the
|
various NXDN reflectors. It speaks the same language as Icom repeaters to the
|
||||||
MMDVM so can be used as a gateway for Icom NXDN repeaters. It also
|
MMDVM so can be used as a gateway for Icom NXDN repeaters. It also
|
||||||
|
|
Loading…
Reference in New Issue