astyle -A1 -T -O

pull/14/head
Tom Early 5 years ago
parent ffa9687463
commit 110a70a27a

@ -90,8 +90,10 @@ std::string CCacheManager::findServerUser()
{ {
std::string suser; std::string suser;
mux.lock(); mux.lock();
for (auto it=NameNick.begin(); it!=NameNick.end(); it++) { for (auto it=NameNick.begin(); it!=NameNick.end(); it++)
if (0 == it->first.compare(0, 2, "s-")) { {
if (0 == it->first.compare(0, 2, "s-"))
{
suser.assign(it->first); suser.assign(it->first);
break; break;
} }
@ -109,14 +111,16 @@ void CCacheManager::updateUser(const std::string &user, const std::string &rptr,
if (! time.empty()) if (! time.empty())
UserTime[user] = time; UserTime[user] = time;
if (rptr.empty()) { if (rptr.empty())
{
mux.unlock(); mux.unlock();
return; return;
} }
UserRptr[user] = rptr; UserRptr[user] = rptr;
if (gate.empty() || addr.empty()) { if (gate.empty() || addr.empty())
{
mux.unlock(); mux.unlock();
return; return;
} }
@ -135,7 +139,8 @@ void CCacheManager::updateRptr(const std::string &rptr, const std::string &gate,
mux.lock(); mux.lock();
RptrGate[rptr] = gate; RptrGate[rptr] = gate;
if (addr.empty()) { if (addr.empty())
{
mux.unlock(); mux.unlock();
return; return;
} }
@ -149,7 +154,8 @@ void CCacheManager::updateGate(const std::string &G, const std::string &addr)
return; return;
std::string gate(G); std::string gate(G);
auto p = gate.find('_'); auto p = gate.find('_');
while (gate.npos != p) { while (gate.npos != p)
{
gate[p] = ' '; gate[p] = ' ';
p = gate.find('_'); p = gate.find('_');
} }
@ -207,10 +213,12 @@ std::string CCacheManager::findRptrGate(const std::string &rptr)
if (rptr.empty()) if (rptr.empty())
return gate; return gate;
auto it = RptrGate.find(rptr); auto it = RptrGate.find(rptr);
if (it == RptrGate.end()) { if (it == RptrGate.end())
{
gate.assign(rptr); gate.assign(rptr);
gate[7] = 'G'; gate[7] = 'G';
} else }
else
gate.assign(it->second); gate.assign(it->second);
return gate; return gate;
} }

@ -22,7 +22,8 @@
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
class CCacheManager { class CCacheManager
{
public: public:
CCacheManager() {} CCacheManager() {}
~CCacheManager() {} ~CCacheManager() {}

@ -32,8 +32,8 @@
#include "Utilities.h" #include "Utilities.h"
CDPlusAuthenticator::CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address) : CDPlusAuthenticator::CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address) :
m_loginCallsign(loginCallsign), m_loginCallsign(loginCallsign),
m_address(address) m_address(address)
{ {
assert(loginCallsign.size()); assert(loginCallsign.size());
@ -48,7 +48,8 @@ int CDPlusAuthenticator::Process(CQnetDB &db, const bool reflectors, const bool
// return true if everything went okay // return true if everything went okay
{ {
int result = client.Open(m_address, AF_UNSPEC, "20001"); int result = client.Open(m_address, AF_UNSPEC, "20001");
if (result) { if (result)
{
fprintf(stderr, "DPlus Authorization failed: %s\n", gai_strerror(result)); fprintf(stderr, "DPlus Authorization failed: %s\n", gai_strerror(result));
return 0; return 0;
} }
@ -70,7 +71,8 @@ int CDPlusAuthenticator::authenticate(CQnetDB &db, const bool reflectors, const
::memcpy(buffer+28, "W7IB2", 5); ::memcpy(buffer+28, "W7IB2", 5);
::memcpy(buffer+40, "DHS0257", 7); ::memcpy(buffer+40, "DHS0257", 7);
if (client.Write(buffer, 56U)) { if (client.Write(buffer, 56U))
{
fprintf(stderr, "ERROR: could not write opening phrase\n"); fprintf(stderr, "ERROR: could not write opening phrase\n");
client.Close(); client.Close();
return 0; return 0;
@ -80,21 +82,25 @@ int CDPlusAuthenticator::authenticate(CQnetDB &db, const bool reflectors, const
unsigned int rval = 0; unsigned int rval = 0;
CHostQueue hqueue; CHostQueue hqueue;
while (ret == 2) { while (ret == 2)
{
unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U]; unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U];
// Ensure that we get exactly len - 2U bytes from the TCP stream // Ensure that we get exactly len - 2U bytes from the TCP stream
ret = client.ReadExact(buffer + 2U, len - 2U); ret = client.ReadExact(buffer + 2U, len - 2U);
if (0 > ret) { if (0 > ret)
{
fprintf(stderr, "Problem reading line, it returned %d\n", errno); fprintf(stderr, "Problem reading line, it returned %d\n", errno);
return rval; return rval;
} }
if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U) { if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U)
{
fprintf(stderr, "Invalid packet received from 20001\n"); fprintf(stderr, "Invalid packet received from 20001\n");
return rval; return rval;
} }
for (unsigned int i = 8U; (i + 25U) < len; i += 26U) { for (unsigned int i = 8U; (i + 25U) < len; i += 26U)
{
std::string address((char *)(buffer + i)); std::string address((char *)(buffer + i));
std::string name((char *)(buffer + i + 16U)); std::string name((char *)(buffer + i + 16U));
@ -106,11 +112,15 @@ int CDPlusAuthenticator::authenticate(CQnetDB &db, const bool reflectors, const
bool active = (buffer[i + 25U] & 0x80U) == 0x80U; bool active = (buffer[i + 25U] & 0x80U) == 0x80U;
// An empty name or IP address or an inactive gateway/reflector is not added // An empty name or IP address or an inactive gateway/reflector is not added
if (address.size()>0U && name.size()>0U && active) { if (address.size()>0U && name.size()>0U && active)
if (reflectors && 0==name.compare(0, 3, "REF")) { {
if (reflectors && 0==name.compare(0, 3, "REF"))
{
rval++; rval++;
hqueue.Push(CHost(name.c_str(), address.c_str(), 20001)); hqueue.Push(CHost(name.c_str(), address.c_str(), 20001));
} else if (repeaters && name.compare(0, 3, "REF")) { }
else if (repeaters && name.compare(0, 3, "REF"))
{
rval++; rval++;
hqueue.Push(CHost(name.c_str(), address.c_str(), 20001)); hqueue.Push(CHost(name.c_str(), address.c_str(), 20001));
} }

@ -25,7 +25,8 @@
#include "TCPReaderWriterClient.h" #include "TCPReaderWriterClient.h"
#include "QnetDB.h" #include "QnetDB.h"
class CDPlusAuthenticator { class CDPlusAuthenticator
{
public: public:
CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address); CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address);
~CDPlusAuthenticator(); ~CDPlusAuthenticator();

@ -26,7 +26,8 @@
#define MASK12 0xfffff800 /* auxiliary vector for testing */ #define MASK12 0xfffff800 /* auxiliary vector for testing */
#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ #define GENPOL 0x00000c75 /* generator polinomial, g(x) */
static const int bit_pos1[] = { static const int bit_pos1[] =
{
0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2,
0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2,
0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2,
@ -42,7 +43,8 @@ static const int bit_pos1[] = {
0, 0, 1, 1, 2, 2 0, 0, 1, 1, 2, 2
}; };
static const int bit_pos2[] = { static const int bit_pos2[] =
{
23, 11, 23, 11, 23, 11, 23, 11, 23, 11, 23, 11,
22, 10, 22, 10, 22, 10, 22, 10, 22, 10, 22, 10,
21, 9, 21, 9, 21, 9, 21, 9, 21, 9, 21, 9,
@ -66,7 +68,8 @@ CDStarDecode::CDStarDecode(void)
decoding_table[0] = 0; decoding_table[0] = 0;
decoding_table[1] = 1; decoding_table[1] = 1;
temp = 1; temp = 1;
for (i=2; i<= 23; i++) { for (i=2; i<= 23; i++)
{
temp = temp << 1; temp = temp << 1;
decoding_table[get_syndrome(temp)] = temp; decoding_table[get_syndrome(temp)] = temp;
} }
@ -75,7 +78,8 @@ CDStarDecode::CDStarDecode(void)
a[2] = 2; a[2] = 2;
temp = arr2int(a,2); temp = arr2int(a,2);
decoding_table[get_syndrome(temp)] = temp; decoding_table[get_syndrome(temp)] = temp;
for (i=1; i<253; i++) { for (i=1; i<253; i++)
{
nextcomb(23,2,a); nextcomb(23,2,a);
temp = arr2int(a,2); temp = arr2int(a,2);
decoding_table[get_syndrome(temp)] = temp; decoding_table[get_syndrome(temp)] = temp;
@ -86,13 +90,15 @@ CDStarDecode::CDStarDecode(void)
a[3] = 3; a[3] = 3;
temp = arr2int(a,3); temp = arr2int(a,3);
decoding_table[get_syndrome(temp)] = temp; decoding_table[get_syndrome(temp)] = temp;
for (i=1; i<1771; i++) { for (i=1; i<1771; i++)
{
nextcomb(23,3,a); nextcomb(23,3,a);
temp = arr2int(a,3); temp = arr2int(a,3);
decoding_table[get_syndrome(temp)] = temp; decoding_table[get_syndrome(temp)] = temp;
} }
for (i=0; i < 4096; i++) { for (i=0; i < 4096; i++)
{
int mask = 0x800000; int mask = 0x800000;
int j; int j;
int pr; int pr;
@ -100,10 +106,12 @@ CDStarDecode::CDStarDecode(void)
prng[i] = 0; prng[i] = 0;
pr = i << 4; pr = i << 4;
for (j=0; j < 24; j++) { for (j=0; j < 24; j++)
{
pr = ((173 * pr) + 13849) & 0xFFFF; pr = ((173 * pr) + 13849) & 0xFFFF;
if ((pr & 0x8000) != 0) { if ((pr & 0x8000) != 0)
{
prng[i] |= mask; prng[i] |= mask;
} }
@ -121,7 +129,8 @@ long CDStarDecode::arr2int(int *a, int r)
int i; int i;
long mul, result = 0, temp; long mul, result = 0, temp;
for (i=1; i<=r; i++) { for (i=1; i<=r; i++)
{
mul = 1; mul = 1;
temp = a[i]-1; temp = a[i]-1;
while (temp--) while (temp--)
@ -164,7 +173,8 @@ long CDStarDecode::get_syndrome(long pattern)
long aux = X22; long aux = X22;
if (pattern >= X11) if (pattern >= X11)
while (pattern & MASK12) { while (pattern & MASK12)
{
while (!(aux & pattern)) while (!(aux & pattern))
aux = aux >> 1; aux = aux >> 1;
pattern ^= (aux/X11) * GENPOL; pattern ^= (aux/X11) * GENPOL;
@ -181,22 +191,26 @@ int CDStarDecode::golay2412(int data, int *decoded)
int parity_corr = 0; int parity_corr = 0;
int i; int i;
for (i = 0; i < 23; i++) { for (i = 0; i < 23; i++)
{
int mask = 1 << i; int mask = 1 << i;
int bit_rcvd = block & mask; int bit_rcvd = block & mask;
int bit_corr = corrected_block & mask; int bit_corr = corrected_block & mask;
if (bit_corr != 0) { if (bit_corr != 0)
{
parity_corr ++; parity_corr ++;
} }
if (bit_rcvd != bit_corr) { if (bit_rcvd != bit_corr)
{
errs ++; errs ++;
} }
} }
if ((parity_corr & 0x01) != (data & 0x01)) { if ((parity_corr & 0x01) != (data & 0x01))
{
errs ++; errs ++;
} }
@ -211,11 +225,13 @@ int CDStarDecode::Decode(const unsigned char *d, int data[3])
int i; int i;
int errs; int errs;
for (i=0; i < 3; i++) { for (i=0; i < 3; i++)
{
bits[i] = 0; bits[i] = 0;
} }
for (i=0; i < 72; i++) { for (i=0; i < 72; i++)
{
bits[ bit_pos1[i] ] |= (d[ i >> 3 ] & (0x80 >> (i & 0x07))) ? (1 << bit_pos2[i]) : 0; bits[ bit_pos1[i] ] |= (d[ i >> 3 ] & (0x80 >> (i & 0x07))) ? (1 << bit_pos2[i]) : 0;
} }

@ -20,7 +20,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
class CDStarDecode { class CDStarDecode
{
public: public:
CDStarDecode(); CDStarDecode();
~CDStarDecode() {} ~CDStarDecode() {}

@ -41,8 +41,10 @@ bool CDVAPDongle::Initialize(const char *serialno, const int frequency, const in
bool ok = false; bool ok = false;
char device[128]; char device[128];
do { do
for (int i = 0; i < 32; i++) { {
for (int i = 0; i < 32; i++)
{
sprintf(device, "/dev/ttyUSB%d", i); sprintf(device, "/dev/ttyUSB%d", i);
if (access(device, R_OK | W_OK) != 0) if (access(device, R_OK | W_OK) != 0)
@ -52,7 +54,8 @@ bool CDVAPDongle::Initialize(const char *serialno, const int frequency, const in
if (!ok) if (!ok)
continue; continue;
if (flock(serfd, LOCK_EX | LOCK_NB) != 0) { if (flock(serfd, LOCK_EX | LOCK_NB) != 0)
{
close(serfd); close(serfd);
serfd = -1; serfd = -1;
ok = false; ok = false;
@ -62,7 +65,8 @@ bool CDVAPDongle::Initialize(const char *serialno, const int frequency, const in
printf("Device %s now locked for exclusive use\n", device); printf("Device %s now locked for exclusive use\n", device);
ok = get_ser(device, serialno); ok = get_ser(device, serialno);
if (!ok) { if (!ok)
{
close(serfd); close(serfd);
serfd = -1; serfd = -1;
continue; continue;
@ -109,10 +113,13 @@ bool CDVAPDongle::Initialize(const char *serialno, const int frequency, const in
if (!ok) if (!ok)
break; break;
} while (false); }
while (false);
if (!ok) { if (!ok)
if (serfd != -1) { {
if (serfd != -1)
{
Stop(); Stop();
close(serfd); close(serfd);
serfd = -1; serfd = -1;
@ -132,29 +139,31 @@ REPLY_TYPE CDVAPDongle::GetReply(SDVAP_REGISTER &dr)
if (rc != 2) if (rc != 2)
return RT_ERR; return RT_ERR;
switch (dr.header) { switch (dr.header)
case 0x5u: {
case 0x6u: case 0x5u:
case 0x7u: case 0x6u:
case 0x8u: case 0x7u:
case 0xcu: case 0x8u:
case 0xdu: case 0xcu:
case 0x10u: case 0xdu:
case 0x2005u: case 0x10u:
case 0x2007u: case 0x2005u:
case 0x602fu: case 0x2007u:
case 0xa02fu: case 0x602fu:
case 0xc012u: case 0xa02fu:
break; // these are all expected headers case 0xc012u:
default: break; // these are all expected headers
printf("unknown header=0x%d\n", (unsigned)dr.header); default:
if (syncit()) printf("unknown header=0x%d\n", (unsigned)dr.header);
return RT_ERR; if (syncit())
return RT_TIMEOUT; return RT_ERR;
return RT_TIMEOUT;
} }
// read the rest of the register // read the rest of the register
uint16_t len = dr.header & 0x1fff; uint16_t len = dr.header & 0x1fff;
while (off < len) { while (off < len)
{
uint8_t *ptr = (uint8_t *)&dr; uint8_t *ptr = (uint8_t *)&dr;
rc = read_from_dvp(ptr + off, len - off); rc = read_from_dvp(ptr + off, len - off);
if (rc < 0) if (rc < 0)
@ -163,68 +172,71 @@ REPLY_TYPE CDVAPDongle::GetReply(SDVAP_REGISTER &dr)
off += rc; off += rc;
} }
// okay, now we'll parse the register and return its type // okay, now we'll parse the register and return its type
switch (dr.header) { switch (dr.header)
case 0x5u: {
switch (dr.param.control) { case 0x5u:
case 0x18u: switch (dr.param.control)
if (dr.param.byte) {
return RT_START; case 0x18u:
else if (dr.param.byte)
return RT_STOP; return RT_START;
case 0x28u: else
if (0x1u==dr.param.ustr[0]) return RT_STOP;
return RT_MODU; case 0x28u:
break; if (0x1u==dr.param.ustr[0])
case 0x80u: return RT_MODU;
return RT_SQL;
case 0x2au:
if (0x0u==dr.param.ustr[0])
return RT_MODE;
break;
}
break;
case 0x6u:
switch (dr.param.control) {
case 0x138u:
return RT_PWR;
case 0x400u:
return RT_OFF;
}
break;
case 0x7u:
if (0x4u==dr.param.control && 0x1u==dr.param.ustr[0])
return RT_FW;
break;
case 0x8u:
if (0x220u==dr.param.control)
return RT_FREQ;
break; break;
case 0xcu: case 0x80u:
if (0x230u==dr.param.control) return RT_SQL;
return RT_FREQ_LIMIT; case 0x2au:
if (0x0u==dr.param.ustr[0])
return RT_MODE;
break; break;
case 0xdu: }
if (0x2u==dr.param.control) break;
return RT_SER; case 0x6u:
break; switch (dr.param.control)
case 0x10u: {
if (0x1u==dr.param.control) case 0x138u:
return RT_NAME; return RT_PWR;
break; case 0x400u:
case 0x2005u: return RT_OFF;
if (0x118u==dr.param.control) }
return RT_PTT; break;
break; case 0x7u:
case 0x2007u: if (0x4u==dr.param.control && 0x1u==dr.param.ustr[0])
if (0x90u==dr.param.control) return RT_FW;
return RT_STS; break;
break; case 0x8u:
case 0x602fu: if (0x220u==dr.param.control)
return RT_HDR_ACK; return RT_FREQ;
case 0xa02fu: break;
return RT_HDR; case 0xcu:
case 0xc012u: if (0x230u==dr.param.control)
return RT_DAT; return RT_FREQ_LIMIT;
break;
case 0xdu:
if (0x2u==dr.param.control)
return RT_SER;
break;
case 0x10u:
if (0x1u==dr.param.control)
return RT_NAME;
break;
case 0x2005u:
if (0x118u==dr.param.control)
return RT_PTT;
break;
case 0x2007u:
if (0x90u==dr.param.control)
return RT_STS;
break;
case 0x602fu:
return RT_HDR_ACK;
case 0xa02fu:
return RT_HDR;
case 0xc012u:
return RT_DAT;
} }
printf("Unrecognized data from dvap: header=%#x control=%#x\n", (unsigned)dr.header, (unsigned)dr.param.control); printf("Unrecognized data from dvap: header=%#x control=%#x\n", (unsigned)dr.header, (unsigned)dr.param.control);
if (syncit()) if (syncit())
@ -244,22 +256,28 @@ bool CDVAPDongle::syncit()
dvapreg.header = 0x2007u; dvapreg.header = 0x2007u;
dvapreg.param.control = 0x90u; dvapreg.param.control = 0x90u;
while (memcmp(data, &dvapreg, 4) != 0) { while (memcmp(data, &dvapreg, 4) != 0)
{
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(serfd, &fds); FD_SET(serfd, &fds);
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 1000; tv.tv_usec = 1000;
int n = select(serfd + 1, &fds, NULL, NULL, &tv); int n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n <= 0) { if (n <= 0)
{
cnt ++; cnt ++;
if (cnt > 100) { if (cnt > 100)
{
printf("syncit() uncessful...stopping\n"); printf("syncit() uncessful...stopping\n");
return true; return true;
} }
} else { }
else
{
unsigned char c; unsigned char c;
n = read_from_dvp(&c, 1); n = read_from_dvp(&c, 1);
if (n > 0) { if (n > 0)
{
data[0] = data[1]; data[0] = data[1];
data[1] = data[2]; data[1] = data[2];
data[2] = data[3]; data[2] = data[3];
@ -284,23 +302,28 @@ bool CDVAPDongle::get_ser(const char *dvp, const char *dvap_serial_number)
dvapreg.param.control = 0x2u; dvapreg.param.control = 0x2u;
int rc = write_to_dvp(&dvapreg, 4); int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) { if (rc != 4)
{
printf("Failed to send request to get dvap serial#\n"); printf("Failed to send request to get dvap serial#\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap serial#\n"); printf("Reached max number of requests to receive dvap serial#\n");
return false; return false;
} }
} while (reply != RT_SER); }
while (reply != RT_SER);
if (0 == strcmp(dvapreg.param.sstr, dvap_serial_number)) { if (0 == strcmp(dvapreg.param.sstr, dvap_serial_number))
{
printf("Using %s: %s, because serial number matches your configuration file\n", dvp, dvap_serial_number); printf("Using %s: %s, because serial number matches your configuration file\n", dvp, dvap_serial_number);
return true; return true;
} }
@ -316,23 +339,28 @@ bool CDVAPDongle::get_name()
dvapreg.param.control = 0x1u; dvapreg.param.control = 0x1u;
int rc = write_to_dvp(&dvapreg, 4); int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) { if (rc != 4)
{
printf("Failed to send request to get dvap name\n"); printf("Failed to send request to get dvap name\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap name\n"); printf("Reached max number of requests to receive dvap name\n");
return false; return false;
} }
} while (reply != RT_NAME); }
while (reply != RT_NAME);
if (0x10u!=dvapreg.header || 0x1u!=dvapreg.param.control || strncmp(dvapreg.param.sstr, "DVAP Dongle", 11)) { if (0x10u!=dvapreg.header || 0x1u!=dvapreg.param.control || strncmp(dvapreg.param.sstr, "DVAP Dongle", 11))
{
printf("Failed to receive dvap name, got %s\n", dvapreg.param.sstr); printf("Failed to receive dvap name, got %s\n", dvapreg.param.sstr);
return false; return false;
} }
@ -350,21 +378,25 @@ bool CDVAPDongle::get_fw()
dvapreg.param.ustr[0] = 0x1u; dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5); int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) { if (rc != 5)
{
printf("Failed to send request to get dvap fw\n"); printf("Failed to send request to get dvap fw\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap fw\n"); printf("Reached max number of requests to receive dvap fw\n");
return false; return false;
} }
} while (reply != RT_FW); }
while (reply != RT_FW);
unsigned int ver = dvapreg.param.ustr[1] + 256 * dvapreg.param.ustr[2]; unsigned int ver = dvapreg.param.ustr[1] + 256 * dvapreg.param.ustr[2];
printf("dvap fw ver: %u.%u\n", ver / 100, ver % 100); printf("dvap fw ver: %u.%u\n", ver / 100, ver % 100);
@ -381,22 +413,26 @@ bool CDVAPDongle::set_modu()
dvapreg.param.ustr[0] = 0x1u; dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5); int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) { if (rc != 5)
{
printf("Failed to send request to set dvap modulation\n"); printf("Failed to send request to set dvap modulation\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap modulation\n"); printf("Reached max number of requests to set dvap modulation\n");
return false; return false;
} }
} while (reply != RT_MODU); }
while (reply != RT_MODU);
return true; return true;
} }
@ -410,22 +446,26 @@ bool CDVAPDongle::set_mode()
dvapreg.param.ustr[0] = 0x0u; dvapreg.param.ustr[0] = 0x0u;
int rc = write_to_dvp(&dvapreg, 5); int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) { if (rc != 5)
{
printf("Failed to send request to set dvap mode\n"); printf("Failed to send request to set dvap mode\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap mode\n"); printf("Reached max number of requests to set dvap mode\n");
return false; return false;
} }
} while (reply != RT_MODE); }
while (reply != RT_MODE);
return true; return true;
} }
@ -437,32 +477,39 @@ bool CDVAPDongle::set_sql(int squelch)
dvapreg.header = 0x5u; dvapreg.header = 0x5u;
dvapreg.param.control = 0x80u; dvapreg.param.control = 0x80u;
if (squelch < -128) { if (squelch < -128)
{
printf("Squelch setting of %d too small, resetting...\n", squelch); printf("Squelch setting of %d too small, resetting...\n", squelch);
squelch = -128; squelch = -128;
} else if (squelch > -45) { }
else if (squelch > -45)
{
printf("Squelch setting of %d too large, resetting...\n", squelch); printf("Squelch setting of %d too large, resetting...\n", squelch);
squelch = -45; squelch = -45;
} }
dvapreg.param.byte = (int8_t)squelch; dvapreg.param.byte = (int8_t)squelch;
int rc = write_to_dvp(&dvapreg, 5); int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) { if (rc != 5)
{
printf("Failed to send request to set dvap sql\n"); printf("Failed to send request to set dvap sql\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap sql\n"); printf("Reached max number of requests to set dvap sql\n");
return false; return false;
} }
} while (reply != RT_SQL); }
while (reply != RT_SQL);
printf("DVAP squelch is %d dB\n", (int)dvapreg.param.byte); printf("DVAP squelch is %d dB\n", (int)dvapreg.param.byte);
return true; return true;
} }
@ -474,32 +521,39 @@ bool CDVAPDongle::set_pwr(int power)
dvapreg.header = 0x6u; dvapreg.header = 0x6u;
dvapreg.param.control = 0x138u; dvapreg.param.control = 0x138u;
if (power < -12) { if (power < -12)
{
printf("Power setting of %d is too low, resetting...\n", power); printf("Power setting of %d is too low, resetting...\n", power);
power = -12; power = -12;
} else if (power > 10) { }
else if (power > 10)
{
printf("Power setting of %d is too high, resetting...\n", power); printf("Power setting of %d is too high, resetting...\n", power);
power = 10; power = 10;
} }
dvapreg.param.word = (int16_t)power; dvapreg.param.word = (int16_t)power;
int rc = write_to_dvp(&dvapreg, 6); int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6) { if (rc != 6)
{
printf("Failed to send request to set dvap pwr\n"); printf("Failed to send request to set dvap pwr\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap pwr\n"); printf("Reached max number of requests to set dvap pwr\n");
return false; return false;
} }
} while (reply != RT_PWR); }
while (reply != RT_PWR);
printf("DVAP power is %d dB\n", (int)dvapreg.param.word); printf("DVAP power is %d dB\n", (int)dvapreg.param.word);
return true; return true;
} }
@ -511,32 +565,39 @@ bool CDVAPDongle::set_off(int offset)
dvapreg.header = 0x6u; dvapreg.header = 0x6u;
dvapreg.param.control = 0x400u; dvapreg.param.control = 0x400u;
if (offset < -2000) { if (offset < -2000)
{
printf("Offset of %d is too low, resetting...\n", offset); printf("Offset of %d is too low, resetting...\n", offset);
offset = -2000; offset = -2000;
} else if (offset > 2000) { }
else if (offset > 2000)
{
printf("Offset of %d is too high, resetting...\n", offset); printf("Offset of %d is too high, resetting...\n", offset);
offset = 2000; offset = 2000;
} }
dvapreg.param.word = (int16_t)offset; dvapreg.param.word = (int16_t)offset;
int rc = write_to_dvp(&dvapreg, 6); int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6) { if (rc != 6)
{
printf("Failed to send request to set dvap offset\n"); printf("Failed to send request to set dvap offset\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap offset\n"); printf("Reached max number of requests to set dvap offset\n");
return false; return false;
} }
} while (reply != RT_OFF); }
while (reply != RT_OFF);
printf("DVAP offset is %d Hz\n", (int)dvapreg.param.word); printf("DVAP offset is %d Hz\n", (int)dvapreg.param.word);
return true; return true;
} }
@ -551,29 +612,36 @@ bool CDVAPDongle::set_freq(int frequency)
dvapreg.param.control = 0x230u; dvapreg.param.control = 0x230u;
int rc = write_to_dvp(&dvapreg, 4); int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4) { if (rc != 4)
{
printf("Failed to send request for frequency limits\n"); printf("Failed to send request for frequency limits\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt++; cnt++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests for dvap frequency limits\n"); printf("Reached max number of requests for dvap frequency limits\n");
return false; return false;
} }
} while (reply != RT_FREQ_LIMIT); }
while (reply != RT_FREQ_LIMIT);
printf("DVAP Frequency limits are from %d to %d Hz\n", dvapreg.param.twod[0], dvapreg.param.twod[1]); printf("DVAP Frequency limits are from %d to %d Hz\n", dvapreg.param.twod[0], dvapreg.param.twod[1]);
// okay, now we know the frequency limits, get on with the show... // okay, now we know the frequency limits, get on with the show...
if (frequency < dvapreg.param.twod[0]) { if (frequency < dvapreg.param.twod[0])
{
printf("Frequency of %d is too small, resetting...\n", frequency); printf("Frequency of %d is too small, resetting...\n", frequency);
frequency = dvapreg.param.twod[0]; frequency = dvapreg.param.twod[0];
} else if (frequency > dvapreg.param.twod[1]) { }
else if (frequency > dvapreg.param.twod[1])
{
printf("Frequency of %d is too large, resetting...\n", frequency); printf("Frequency of %d is too large, resetting...\n", frequency);
frequency = dvapreg.param.twod[1]; frequency = dvapreg.param.twod[1];
} }
@ -584,22 +652,26 @@ bool CDVAPDongle::set_freq(int frequency)
dvapreg.param.dword = frequency; dvapreg.param.dword = frequency;
rc = write_to_dvp(&dvapreg, 8); rc = write_to_dvp(&dvapreg, 8);
if (rc != 8) { if (rc != 8)
{
printf("Failed to send request to set dvap frequency\n"); printf("Failed to send request to set dvap frequency\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap frequency\n"); printf("Reached max number of requests to set dvap frequency\n");
return false; return false;
} }
} while (reply != RT_FREQ); }
while (reply != RT_FREQ);
printf("DVAP frequency is %d Hz\n", dvapreg.param.dword); printf("DVAP frequency is %d Hz\n", dvapreg.param.dword);
return true; return true;
} }
@ -614,22 +686,26 @@ bool CDVAPDongle::start_dvap()
dvapreg.param.byte = 0x1; dvapreg.param.byte = 0x1;
int rc = write_to_dvp(&dvapreg, 5); int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5) { if (rc != 5)
{
printf("Failed to send request to start the dvap dongle\n"); printf("Failed to send request to start the dvap dongle\n");
return false; return false;
} }
do { do
{
usleep(5000); usleep(5000);
reply = GetReply(dvapreg); reply = GetReply(dvapreg);
cnt ++; cnt ++;
if (cnt >= MAX_REPL_CNT) { if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to start the dvap dongle\n"); printf("Reached max number of requests to start the dvap dongle\n");
return false; return false;
} }
} while (reply != RT_START); }
while (reply != RT_START);
return true; return true;
} }
@ -649,9 +725,11 @@ int CDVAPDongle::write_to_dvp(const void *buffer, const unsigned int len)
return 0; return 0;
uint8_t *buf = (uint8_t *)buffer; uint8_t *buf = (uint8_t *)buffer;
while (ptr < len) { while (ptr < len)
{
ssize_t n = write(serfd, buf + ptr, len - ptr); ssize_t n = write(serfd, buf + ptr, len - ptr);
if (n < 0) { if (n < 0)
{
printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno)); printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1; return -1;
} }
@ -675,25 +753,30 @@ int CDVAPDongle::read_from_dvp(void *buffer, unsigned int len)
if (len == 0) if (len == 0)
return 0; return 0;
while (off < len) { while (off < len)
{
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(serfd, &fds); FD_SET(serfd, &fds);
if (off == 0) { if (off == 0)
{
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 0; tv.tv_usec = 0;
n = select(serfd + 1, &fds, NULL, NULL, &tv); n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n == 0) if (n == 0)
return 0; // nothing to read from the dvap return 0; // nothing to read from the dvap
} else }
else
n = select(serfd + 1, &fds, NULL, NULL, NULL); n = select(serfd + 1, &fds, NULL, NULL, NULL);
if (n < 0) { if (n < 0)
{
printf("select error=%d on dvap\n", errno); printf("select error=%d on dvap\n", errno);
return -1; return -1;
} }
if (n > 0) { if (n > 0)
{
temp_len = read(serfd, buf + off, len - off); temp_len = read(serfd, buf + off, len - off);
if (temp_len > 0) if (temp_len > 0)
off += temp_len; off += temp_len;
@ -708,19 +791,22 @@ bool CDVAPDongle::OpenSerial(char *device)
static termios t; static termios t;
serfd = open(device, O_RDWR | O_NOCTTY | O_NDELAY, 0); serfd = open(device, O_RDWR | O_NOCTTY | O_NDELAY, 0);
if (serfd < 0) { if (serfd < 0)
{
printf("Failed to open device [%s], error=%d, message=%s\n", device, errno, strerror(errno)); printf("Failed to open device [%s], error=%d, message=%s\n", device, errno, strerror(errno));
return false; return false;
} }
if (isatty(serfd) == 0) { if (isatty(serfd) == 0)
{
printf("Device %s is not a tty device\n", device); printf("Device %s is not a tty device\n", device);
close(serfd); close(serfd);
serfd = -1; serfd = -1;
return false; return false;
} }
if (tcgetattr(serfd, &t) < 0) { if (tcgetattr(serfd, &t) < 0)
{
printf("tcgetattr failed for %s, error=%d, message-%s\n", device, errno, strerror(errno)); printf("tcgetattr failed for %s, error=%d, message-%s\n", device, errno, strerror(errno));
close(serfd); close(serfd);
serfd = -1; serfd = -1;
@ -738,7 +824,8 @@ bool CDVAPDongle::OpenSerial(char *device)
cfsetospeed(&t, B230400); cfsetospeed(&t, B230400);
cfsetispeed(&t, B230400); cfsetispeed(&t, B230400);
if (tcsetattr(serfd, TCSANOW, &t) < 0) { if (tcsetattr(serfd, TCSANOW, &t) < 0)
{
printf("tcsetattr failed for %s, error=%dm message=%s\n", device, errno, strerror(errno)); printf("tcsetattr failed for %s, error=%dm message=%s\n", device, errno, strerror(errno));
close(serfd); close(serfd);
serfd = -1; serfd = -1;

@ -17,9 +17,10 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <stdint.h> #include <stdint.h>
enum REPLY_TYPE { enum REPLY_TYPE
{
RT_TIMEOUT, RT_TIMEOUT,
RT_ERR, RT_ERR,
RT_UNKNOWN, RT_UNKNOWN,
@ -44,13 +45,17 @@ enum REPLY_TYPE {
}; };
#pragma pack(push,1) #pragma pack(push,1)
using SDVAP_REGISTER = struct dvp_register_tag { using SDVAP_REGISTER = struct dvp_register_tag
{
uint16_t header; uint16_t header;
union { union
{
uint8_t nul; uint8_t nul;
struct { struct
{
uint16_t control; uint16_t control;
union { union
{
int8_t byte; int8_t byte;
int16_t word; int16_t word;
int32_t dword; int32_t dword;
@ -59,12 +64,15 @@ using SDVAP_REGISTER = struct dvp_register_tag {
uint8_t ustr[12]; uint8_t ustr[12];
}; };
} param; } param;
struct { struct
{
uint16_t streamid; uint16_t streamid;
uint8_t framepos; uint8_t framepos;
uint8_t seq; uint8_t seq;
union { union
struct { {
struct
{
unsigned char flag[3]; unsigned char flag[3];
unsigned char rpt2[8]; unsigned char rpt2[8];
unsigned char rpt1[8]; unsigned char rpt1[8];
@ -73,7 +81,8 @@ using SDVAP_REGISTER = struct dvp_register_tag {
unsigned char sfx[4]; unsigned char sfx[4];
unsigned char pfcs[2]; unsigned char pfcs[2];
} hdr; } hdr;
struct { struct
{
unsigned char voice; unsigned char voice;
unsigned char sdata; unsigned char sdata;
} vad; } vad;
@ -85,36 +94,36 @@ using SDVAP_REGISTER = struct dvp_register_tag {
class CDVAPDongle class CDVAPDongle
{ {
public: public:
CDVAPDongle(); CDVAPDongle();
~CDVAPDongle(); ~CDVAPDongle();
bool Initialize(const char *serialno, const int frequency, const int offset, const int power, const int squelch); bool Initialize(const char *serialno, const int frequency, const int offset, const int power, const int squelch);
REPLY_TYPE GetReply(SDVAP_REGISTER &dr); REPLY_TYPE GetReply(SDVAP_REGISTER &dr);
void Stop(); void Stop();
int KeepAlive(); int KeepAlive();
void SendRegister(SDVAP_REGISTER &dr); void SendRegister(SDVAP_REGISTER &dr);
private: private:
// data // data
int serfd; int serfd;
const unsigned int MAX_REPL_CNT; const unsigned int MAX_REPL_CNT;
uint32_t frequency; uint32_t frequency;
int32_t offset; int32_t offset;
SDVAP_REGISTER dvapreg; SDVAP_REGISTER dvapreg;
// functions // functions
bool OpenSerial(char *device); bool OpenSerial(char *device);
int read_from_dvp(void* buf, unsigned int len); int read_from_dvp(void* buf, unsigned int len);
int write_to_dvp(const void* buf, const unsigned int len); int write_to_dvp(const void* buf, const unsigned int len);
bool syncit(); bool syncit();
bool get_ser(const char *dvp, const char *dvap_serial_number); bool get_ser(const char *dvp, const char *dvap_serial_number);
bool get_name(); bool get_name();
bool get_fw(); bool get_fw();
bool set_modu(); bool set_modu();
bool set_mode(); bool set_mode();
bool set_sql(int squelch); bool set_sql(int squelch);
bool set_pwr(int power); bool set_pwr(int power);
bool set_off(int offset); bool set_off(int offset);
bool set_freq(int frequency); bool set_freq(int frequency);
bool start_dvap(); bool start_dvap();
}; };

@ -21,7 +21,8 @@
#include <queue> #include <queue>
#include <string> #include <string>
class CHost { class CHost
{
public: public:
CHost() {} CHost() {}

@ -42,15 +42,16 @@ void CKRBase::SetState(bool state)
void CKRBase::SigHandler(int sig) void CKRBase::SigHandler(int sig)
{ {
switch (sig) { switch (sig)
case SIGINT: {
case SIGHUP: case SIGINT:
case SIGTERM: case SIGHUP:
keep_running = false; case SIGTERM:
break; keep_running = false;
default: break;
std::cerr << "caught an unexpected signal=" << sig << std::endl; default:
break; std::cerr << "caught an unexpected signal=" << sig << std::endl;
break;
} }
} }

@ -38,13 +38,15 @@ bool CLocation::Parse(const char *instr)
if (s.size() < 20) if (s.size() < 20)
return false; return false;
if (! std::regex_search(s.c_str(), cm, gps, std::regex_constants::match_default)) { if (! std::regex_search(s.c_str(), cm, gps, std::regex_constants::match_default))
{
//std::cerr << "Unsuccessful gps parse of '" << s << "'" << std::endl; //std::cerr << "Unsuccessful gps parse of '" << s << "'" << std::endl;
return false; return false;
} }
auto size = cm.size(); auto size = cm.size();
if (size != 7) { if (size != 7)
{
std::cerr << "Bad CRC Match for " << s << ":"; std::cerr << "Bad CRC Match for " << s << ":";
for (unsigned i=0; i<size; i++) for (unsigned i=0; i<size; i++)
std::cerr << " [" << cm[i] << "]"; std::cerr << " [" << cm[i] << "]";
@ -53,13 +55,15 @@ bool CLocation::Parse(const char *instr)
} }
double deg = stod(cm[1]); double deg = stod(cm[1]);
if (90.0 < deg) { if (90.0 < deg)
{
std::cout << "Latitude degree " << deg << " is out of range" << std::endl; std::cout << "Latitude degree " << deg << " is out of range" << std::endl;
return false; return false;
} }
double min = stod(cm[2]); double min = stod(cm[2]);
if (60.0 < min) { if (60.0 < min)
{
std::cout << "Latitude minutes " << min << " is out of range" << std::endl; std::cout << "Latitude minutes " << min << " is out of range" << std::endl;
return false; return false;
} }
@ -69,13 +73,15 @@ bool CLocation::Parse(const char *instr)
latitude = 0.0 - latitude; latitude = 0.0 - latitude;
deg = stod(cm[4]); deg = stod(cm[4]);
if (180.0 < deg) { if (180.0 < deg)
{
std::cout << "Longitude degree " << deg << " is out of range" << std::endl; std::cout << "Longitude degree " << deg << " is out of range" << std::endl;
return false; return false;
} }
min = stod(cm[5]); min = stod(cm[5]);
if (60.0 < min) { if (60.0 < min)
{
std::cout << "Longitude minutes " << min << " is out of range" << std::endl; std::cout << "Longitude minutes " << min << " is out of range" << std::endl;
return false; return false;
} }
@ -103,7 +109,8 @@ const char *CLocation::APRS(std::string &call, const char *station)
call.resize(8, ' '); call.resize(8, ' ');
last = call.at(7); last = call.at(7);
auto pos = call.find(' '); auto pos = call.find(' ');
if (call.npos != pos) { if (call.npos != pos)
{
call.resize(pos+1); call.resize(pos+1);
} }
double latmin, lonmin; double latmin, lonmin;

@ -43,20 +43,25 @@ char *CQnetConfigure::Trim(char *s)
bool CQnetConfigure::ReadConfigFile(const char *configfile, std::map<std::string, std::string> &amap) bool CQnetConfigure::ReadConfigFile(const char *configfile, std::map<std::string, std::string> &amap)
{ {
FILE *fp = fopen(configfile, "r"); FILE *fp = fopen(configfile, "r");
if (fp) { if (fp)
{
char line[2048]; char line[2048];
while (fgets(line, 2048, fp)) { while (fgets(line, 2048, fp))
{
char *key = strtok(line, "="); char *key = strtok(line, "=");
key = Trim(key); key = Trim(key);
if (strlen(key) && '#' != *key) { if (strlen(key) && '#' != *key)
{
char *val = strtok(NULL, "\r\n"); char *val = strtok(NULL, "\r\n");
char *val2 = Trim(val); char *val2 = Trim(val);
if ('\'' == val2[0]) { if ('\'' == val2[0])
{
if ('\'' == val2[1]) if ('\'' == val2[1])
val[0] = '\0'; val[0] = '\0';
else else
val = strtok(val2, "'"); val = strtok(val2, "'");
} else }
else
val = strtok(val2, "# \t"); val = strtok(val2, "# \t");
amap[key] = val; amap[key] = val;
} }
@ -95,7 +100,8 @@ bool CQnetConfigure::GetDefaultBool(const std::string &path, const std::string &
dvalue = false; dvalue = false;
else if ('1'==value.at(0) || 't'==value.at(0) || 'T'==value.at(0)) else if ('1'==value.at(0) || 't'==value.at(0) || 'T'==value.at(0))
dvalue = true; dvalue = true;
else { else
{
fprintf(stderr, "%s=%s doesn't seem to be a boolean!\n", path.c_str(), value.c_str()); fprintf(stderr, "%s=%s doesn't seem to be a boolean!\n", path.c_str(), value.c_str());
return true; return true;
} }
@ -123,28 +129,38 @@ bool CQnetConfigure::GetDefaultInt(const std::string &path, const std::string &m
bool CQnetConfigure::GetDefaultString(const std::string &path, const std::string &mod, std::string &dvalue) bool CQnetConfigure::GetDefaultString(const std::string &path, const std::string &mod, std::string &dvalue)
{ {
std::string search, search_again; std::string search, search_again;
if (mod.empty()) { if (mod.empty())
{
search = path + "_d"; // there is no mod, so this is a simple search search = path + "_d"; // there is no mod, so this is a simple search
} else { }
else
{
search_again = mod; // we're looking from a module value. We may have to look for non-generic module parameters search_again = mod; // we're looking from a module value. We may have to look for non-generic module parameters
if (0==path.compare(0, 7, "module_") && ('a'==path.at(7) || 'b'==path.at(7) || 'c'==path.at(7)) && '_'==path.at(8)) { if (0==path.compare(0, 7, "module_") && ('a'==path.at(7) || 'b'==path.at(7) || 'c'==path.at(7)) && '_'==path.at(8))
{
// path begins with module_{a|b|c}_ // path begins with module_{a|b|c}_
if (0==mod.compare("dvrptr") || 0==mod.compare("dvap") || 0==mod.compare("mmdvmhost") || 0==mod.compare("mmdvmmodem") || 0==mod.compare("itap") || 0==mod.compare("thumbdv")) { if (0==mod.compare("dvrptr") || 0==mod.compare("dvap") || 0==mod.compare("mmdvmhost") || 0==mod.compare("mmdvmmodem") || 0==mod.compare("itap") || 0==mod.compare("thumbdv"))
{
// and the module is recognized // and the module is recognized
search = path; search = path;
search.replace(7, 1, 1, 'x'); search.replace(7, 1, 1, 'x');
search_again += path.substr(8); // now the search_again path might look like dvap_frequency, for example. search_again += path.substr(8); // now the search_again path might look like dvap_frequency, for example.
} else { }
else
{
fprintf(stderr, "Unrecognized module type = '%s'\n", mod.c_str()); fprintf(stderr, "Unrecognized module type = '%s'\n", mod.c_str());
return true; return true;
} }
} else { }
else
{
fprintf(stderr, "%s looks like an ilformed request from module '%s'\n", path.c_str(), mod.c_str()); fprintf(stderr, "%s looks like an ilformed request from module '%s'\n", path.c_str(), mod.c_str());
return true; return true;
} }
} }
auto it = defaults.find(search); auto it = defaults.find(search);
if (defaults.end() == it) { if (defaults.end() == it)
{
it = defaults.find(search_again); it = defaults.find(search_again);
if (defaults.end() == it) if (defaults.end() == it)
return true; return true;
@ -156,20 +172,25 @@ bool CQnetConfigure::GetDefaultString(const std::string &path, const std::string
bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, bool &value) bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, bool &value)
{ {
auto it = cfg.find(path); auto it = cfg.find(path);
if (cfg.end() == it) { if (cfg.end() == it)
{
bool dvalue; bool dvalue;
if (GetDefaultBool(path, mod, dvalue)) { if (GetDefaultBool(path, mod, dvalue))
{
fprintf(stderr, "%s not found in either the cfg file or the defaults file!\n", path.c_str()); fprintf(stderr, "%s not found in either the cfg file or the defaults file!\n", path.c_str());
return true; return true;
} }
value = dvalue; // found a value in the defaults value = dvalue; // found a value in the defaults
} else { // found a value in the cfg file }
else // found a value in the cfg file
{
char c = it->second.at(0); char c = it->second.at(0);
if ('0'==c || 'f'==c || 'F'==c) if ('0'==c || 'f'==c || 'F'==c)
value = false; value = false;
else if ('1'==c || 't'==c || 'T'==c) else if ('1'==c || 't'==c || 'T'==c)
value = true; value = true;
else { else
{
fprintf(stderr, "%s=%s doesn't seem to define a boolean\n", path.c_str(), it->second.c_str()); fprintf(stderr, "%s=%s doesn't seem to define a boolean\n", path.c_str(), it->second.c_str());
return true; return true;
} }
@ -181,20 +202,26 @@ bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, b
bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, double &value, const double min, const double max) bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, double &value, const double min, const double max)
{ {
auto it = cfg.find(path); auto it = cfg.find(path);
if (cfg.end() == it) { if (cfg.end() == it)
{
double dvalue; double dvalue;
if (GetDefaultDouble(path, mod, dvalue)) { if (GetDefaultDouble(path, mod, dvalue))
{
fprintf(stderr, "%s not found in either the cfg file or the defaults file!\n", path.c_str()); fprintf(stderr, "%s not found in either the cfg file or the defaults file!\n", path.c_str());
return true; return true;
} }
if (dvalue < min || dvalue > max) { if (dvalue < min || dvalue > max)
{
fprintf(stderr, "Default value %s=%g is out of acceptable range\n", path.c_str(), value); fprintf(stderr, "Default value %s=%g is out of acceptable range\n", path.c_str(), value);
return true; return true;
} }
value = dvalue; value = dvalue;
} else { }
else
{
value = std::stod(it->second); value = std::stod(it->second);
if (value < min || value > max) { if (value < min || value > max)
{
fprintf(stderr, "%s=%g is out of acceptable range\n", path.c_str(), value); fprintf(stderr, "%s=%g is out of acceptable range\n", path.c_str(), value);
return true; return true;
} }
@ -206,20 +233,26 @@ bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, d
bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, int &value, const int min, const int max) bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, int &value, const int min, const int max)
{ {
auto it = cfg.find(path); auto it = cfg.find(path);
if (cfg.end() == it) { if (cfg.end() == it)
{
int dvalue; int dvalue;
if (GetDefaultInt(path, mod, dvalue)) { if (GetDefaultInt(path, mod, dvalue))
{
fprintf(stderr, "%s not found in either the cfg file or the defaults file\n", path.c_str()); fprintf(stderr, "%s not found in either the cfg file or the defaults file\n", path.c_str());
return true; return true;
} }
if (dvalue < min || dvalue > max) { if (dvalue < min || dvalue > max)
{
fprintf(stderr, "Default value %s=%d is out of acceptable range\n", path.c_str(), value); fprintf(stderr, "Default value %s=%d is out of acceptable range\n", path.c_str(), value);
return true; return true;
} }
value = dvalue; value = dvalue;
} else { }
else
{
value = std::stoi(it->second); value = std::stoi(it->second);
if (value < min || value > max) { if (value < min || value > max)
{
fprintf(stderr, "%s=%s is out of acceptable range\n", path.c_str(), it->second.c_str()); fprintf(stderr, "%s=%s is out of acceptable range\n", path.c_str(), it->second.c_str());
return true; return true;
} }
@ -231,22 +264,28 @@ bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, i
bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, std::string &value, int min, int max) bool CQnetConfigure::GetValue(const std::string &path, const std::string &mod, std::string &value, int min, int max)
{ {
auto it = cfg.find(path); auto it = cfg.find(path);
if (cfg.end() == it) { if (cfg.end() == it)
{
std::string dvalue; std::string dvalue;
if (GetDefaultString(path, mod, dvalue)) { if (GetDefaultString(path, mod, dvalue))
{
fprintf(stderr, "%s not found in either the cfg file or the defaults file\n", path.c_str()); fprintf(stderr, "%s not found in either the cfg file or the defaults file\n", path.c_str());
return true; return true;
} }
int l = dvalue.length(); int l = dvalue.length();
if (min-1>=l || l>max) { if (min-1>=l || l>max)
{
printf("Default value %s='%s' is wrong size\n", path.c_str(), value.c_str()); printf("Default value %s='%s' is wrong size\n", path.c_str(), value.c_str());
return true; return true;
} }
value.assign(dvalue); value.assign(dvalue);
} else { }
else
{
value.assign(it->second); value.assign(it->second);
int l = value.length(); int l = value.length();
if (l<min || l>max) { if (l<min || l>max)
{
printf("%s='%s' is wrong size\n", path.c_str(), value.c_str()); printf("%s='%s' is wrong size\n", path.c_str(), value.c_str());
return true; return true;
} }

@ -21,13 +21,14 @@
#include <string> #include <string>
#include <map> #include <map>
class CQnetConfigure { class CQnetConfigure
{
public: public:
CQnetConfigure(); CQnetConfigure();
virtual ~CQnetConfigure(); virtual ~CQnetConfigure();
bool Initialize(const char *configfile); bool Initialize(const char *configfile);
bool GetValue(const std::string &path, const std::string &mod, bool &value); bool GetValue(const std::string &path, const std::string &mod, bool &value);
bool GetValue(const std::string &path, const std::string &mod, double &value, const double min, const double max); bool GetValue(const std::string &path, const std::string &mod, double &value, const double min, const double max);
bool GetValue(const std::string &path, const std::string &mod, int &value, const int min, const int max); bool GetValue(const std::string &path, const std::string &mod, int &value, const int min, const int max);
bool GetValue(const std::string &path, const std::string &mod, std::string &value, const int min, const int max); bool GetValue(const std::string &path, const std::string &mod, std::string &value, const int min, const int max);
bool KeyExists(const std::string &key); bool KeyExists(const std::string &key);
@ -39,7 +40,7 @@ private:
char *Trim(char *s); char *Trim(char *s);
bool ReadConfigFile(const char *file, std::map<std::string, std::string> &amap); bool ReadConfigFile(const char *file, std::map<std::string, std::string> &amap);
bool GetDefaultBool (const std::string &key, const std::string &mod, bool &dval); bool GetDefaultBool (const std::string &key, const std::string &mod, bool &dval);
bool GetDefaultDouble(const std::string &key, const std::string &mod, double &dval); bool GetDefaultDouble(const std::string &key, const std::string &mod, double &dval);
bool GetDefaultInt (const std::string &key, const std::string &mod, int &dval); bool GetDefaultInt (const std::string &key, const std::string &mod, int &dval);
bool GetDefaultString(const std::string &key, const std::string &mod, std::string &dval); bool GetDefaultString(const std::string &key, const std::string &mod, std::string &dval);
}; };

@ -23,7 +23,8 @@
bool CQnetDB::Open(const char *name) bool CQnetDB::Open(const char *name)
{ {
if (sqlite3_open_v2(name, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) { if (sqlite3_open_v2(name, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL))
{
fprintf(stderr, "CQnetDB::Open: can't open %s\n", name); fprintf(stderr, "CQnetDB::Open: can't open %s\n", name);
return true; return true;
} }
@ -36,44 +37,47 @@ bool CQnetDB::Init()
char *eMsg; char *eMsg;
std::string sql("CREATE TABLE IF NOT EXISTS LHEARD(" std::string sql("CREATE TABLE IF NOT EXISTS LHEARD("
"callsign TEXT PRIMARY KEY, " "callsign TEXT PRIMARY KEY, "
"sfx TEXT DEFAULT ' ', " "sfx TEXT DEFAULT ' ', "
"message TEXT DEFAULT ' ', " "message TEXT DEFAULT ' ', "
"maidenhead TEXT DEFAULT ' ', " "maidenhead TEXT DEFAULT ' ', "
"latitude REAL DEFAULT 0.0, " "latitude REAL DEFAULT 0.0, "
"longitude REAL DEFAULT 0.0, " "longitude REAL DEFAULT 0.0, "
"module TEXT, " "module TEXT, "
"reflector TEXT, " "reflector TEXT, "
"lasttime INT NOT NULL" "lasttime INT NOT NULL"
") WITHOUT ROWID;"); ") WITHOUT ROWID;");
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
} }
sql.assign("CREATE TABLE IF NOT EXISTS LINKSTATUS(" sql.assign("CREATE TABLE IF NOT EXISTS LINKSTATUS("
"ip_address TEXT PRIMARY KEY, " "ip_address TEXT PRIMARY KEY, "
"from_mod TEXT NOT NULL, " "from_mod TEXT NOT NULL, "
"to_callsign TEXT NOT NULL, " "to_callsign TEXT NOT NULL, "
"to_mod TEXT NOT NULL, " "to_mod TEXT NOT NULL, "
"linked_time INT NOT NULL" "linked_time INT NOT NULL"
") WITHOUT ROWID;"); ") WITHOUT ROWID;");
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
} }
sql.assign("CREATE TABLE IF NOT EXISTS GATEWAYS(" sql.assign("CREATE TABLE IF NOT EXISTS GATEWAYS("
"name TEXT PRIMARY KEY, " "name TEXT PRIMARY KEY, "
"address TEXT NOT NULL, " "address TEXT NOT NULL, "
"port INT NOT NULL" "port INT NOT NULL"
") WITHOUT ROWID;"); ") WITHOUT ROWID;");
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::Init [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -81,10 +85,11 @@ bool CQnetDB::Init()
return false; return false;
} }
static int countcallback(void *count, int /*argc*/, char **argv, char **/*azColName*/) { static int countcallback(void *count, int /*argc*/, char **argv, char **/*azColName*/)
auto c = (int *)count; {
*c = atoi(argv[0]); auto c = (int *)count;
return 0; *c = atoi(argv[0]);
return 0;
} }
bool CQnetDB::UpdateLH(const char *callsign, const char *sfx, const char module, const char *reflector) bool CQnetDB::UpdateLH(const char *callsign, const char *sfx, const char module, const char *reflector)
@ -98,13 +103,15 @@ bool CQnetDB::UpdateLH(const char *callsign, const char *sfx, const char module,
int count = 0; int count = 0;
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), countcallback, &count, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), countcallback, &count, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateLH [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdateLH [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
} }
if (count) { if (count)
{
sql.assign("UPDATE LHEARD SET (sfx,module,reflector,lasttime) = ('"); sql.assign("UPDATE LHEARD SET (sfx,module,reflector,lasttime) = ('");
sql.append(sfx); sql.append(sfx);
sql.append("','"); sql.append("','");
@ -115,7 +122,9 @@ bool CQnetDB::UpdateLH(const char *callsign, const char *sfx, const char module,
sql.append("strftime('%s','now')) WHERE callsign='"); sql.append("strftime('%s','now')) WHERE callsign='");
sql.append(callsign); sql.append(callsign);
sql.append("';"); sql.append("';");
} else { }
else
{
sql.assign("INSERT INTO LHEARD (callsign,sfx,module,reflector,lasttime) VALUES ('"); sql.assign("INSERT INTO LHEARD (callsign,sfx,module,reflector,lasttime) VALUES ('");
sql.append(callsign); sql.append(callsign);
sql.append("','"); sql.append("','");
@ -128,7 +137,8 @@ bool CQnetDB::UpdateLH(const char *callsign, const char *sfx, const char module,
sql.append("strftime('%s','now'));"); sql.append("strftime('%s','now'));");
} }
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateLH [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdateLH [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -153,7 +163,8 @@ bool CQnetDB::UpdatePosition(const char *callsign, const char *maidenhead, doubl
sql.append("';"); sql.append("';");
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdatePosition [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdatePosition [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -174,7 +185,8 @@ bool CQnetDB::UpdateMessage(const char *callsign, const char *message)
sql.append("';"); sql.append("';");
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateMessage [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdateMessage [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -200,7 +212,8 @@ bool CQnetDB::UpdateLS(const char *address, const char from_mod, const char *to_
sql.append(");"); sql.append(");");
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateLS [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdateLS [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -224,7 +237,8 @@ bool CQnetDB::UpdateGW(const char *name, const char *address, unsigned short por
sql.append(");"); sql.append(");");
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateGW [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::UpdateGW [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -239,18 +253,21 @@ bool CQnetDB::UpdateGW(CHostQueue &hqueue)
return false; return false;
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateGW BEGIN TRANSATION error: %s\n", eMsg); fprintf(stderr, "CQnetDB::UpdateGW BEGIN TRANSATION error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
} }
while (! hqueue.Empty()) { while (! hqueue.Empty())
{
auto h = hqueue.Pop(); auto h = hqueue.Pop();
UpdateGW(h.name.c_str(), h.addr.c_str(), h.port); UpdateGW(h.name.c_str(), h.addr.c_str(), h.port);
} }
if (SQLITE_OK != sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::UpdateGW COMMIT TRANSACTION error: %s\n", eMsg); fprintf(stderr, "CQnetDB::UpdateGW COMMIT TRANSACTION error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -267,7 +284,8 @@ bool CQnetDB::DeleteLS(const char *address)
sql.append("';"); sql.append("';");
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::DeleteLS [%s] error: %s\n", sql.c_str(), eMsg); fprintf(stderr, "CQnetDB::DeleteLS [%s] error: %s\n", sql.c_str(), eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
return true; return true;
@ -286,15 +304,18 @@ bool CQnetDB::FindLS(const char mod, std::list<CLink> &linklist)
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0); int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (SQLITE_OK != rval) { if (SQLITE_OK != rval)
{
fprintf(stderr, "CQnetDB::FindLS [%s] error\n", sql.c_str()); fprintf(stderr, "CQnetDB::FindLS [%s] error\n", sql.c_str());
return true; return true;
} }
while (SQLITE_ROW == sqlite3_step(stmt)) { while (SQLITE_ROW == sqlite3_step(stmt))
{
std::string cs((const char *)sqlite3_column_text(stmt, 1)); std::string cs((const char *)sqlite3_column_text(stmt, 1));
std::string mod((const char *)sqlite3_column_text(stmt, 2)); std::string mod((const char *)sqlite3_column_text(stmt, 2));
if (mod.at(0) != 'p') { if (mod.at(0) != 'p')
{
cs.resize(7, ' '); cs.resize(7, ' ');
cs.append(mod); cs.append(mod);
} }
@ -318,17 +339,21 @@ bool CQnetDB::FindGW(const char *name, std::string &address, unsigned short &por
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0); int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (SQLITE_OK != rval) { if (SQLITE_OK != rval)
{
fprintf(stderr, "CQnetDB::FindGW error: %d\n", rval); fprintf(stderr, "CQnetDB::FindGW error: %d\n", rval);
return true; return true;
} }
if (SQLITE_ROW == sqlite3_step(stmt)) { if (SQLITE_ROW == sqlite3_step(stmt))
{
address.assign((const char *)sqlite3_column_text(stmt, 0)); address.assign((const char *)sqlite3_column_text(stmt, 0));
port = (unsigned short)(sqlite3_column_int(stmt, 1)); port = (unsigned short)(sqlite3_column_int(stmt, 1));
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return false; return false;
} else { }
else
{
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return true; return true;
} }
@ -347,15 +372,19 @@ bool CQnetDB::FindGW(const char *name)
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0); int rval = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (SQLITE_OK != rval) { if (SQLITE_OK != rval)
{
fprintf(stderr, "CQnetDB::FindGW error: %d\n", rval); fprintf(stderr, "CQnetDB::FindGW error: %d\n", rval);
return true; return true;
} }
if (SQLITE_ROW == sqlite3_step(stmt)) { if (SQLITE_ROW == sqlite3_step(stmt))
{
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return true; return true;
} else { }
else
{
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return false; return false;
} }
@ -368,7 +397,8 @@ void CQnetDB::ClearLH()
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM LHEARD;", NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM LHEARD;", NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::ClearLH error: %s\n", eMsg); fprintf(stderr, "CQnetDB::ClearLH error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
} }
@ -381,7 +411,8 @@ void CQnetDB::ClearLS()
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM LINKSTATUS;", NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM LINKSTATUS;", NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::ClearLS error: %s\n", eMsg); fprintf(stderr, "CQnetDB::ClearLS error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
} }
@ -394,7 +425,8 @@ void CQnetDB::ClearGW()
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM GATEWAYS;", NULL, 0, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, "DELETE FROM GATEWAYS;", NULL, 0, &eMsg))
{
fprintf(stderr, "CQnetDB::ClearGW error: %s\n", eMsg); fprintf(stderr, "CQnetDB::ClearGW error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
} }
@ -405,17 +437,18 @@ int CQnetDB::Count(const char *table)
if (NULL == db) if (NULL == db)
return 0; return 0;
std::string sql("SELECT COUNT(*) FROM "); std::string sql("SELECT COUNT(*) FROM ");
sql.append(table); sql.append(table);
sql.append(";"); sql.append(";");
int count = 0; int count = 0;
char *eMsg; char *eMsg;
if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), countcallback, &count, &eMsg)) { if (SQLITE_OK != sqlite3_exec(db, sql.c_str(), countcallback, &count, &eMsg))
{
fprintf(stderr, "CQnetDB::Count error: %s\n", eMsg); fprintf(stderr, "CQnetDB::Count error: %s\n", eMsg);
sqlite3_free(eMsg); sqlite3_free(eMsg);
} }
return count; return count;
} }

@ -24,22 +24,23 @@
#include "HostQueue.h" #include "HostQueue.h"
class CLink { class CLink
{
public: public:
CLink(const std::string &call, const unsigned char *addr, time_t ltime) : callsign(call) , address((const char *)addr) , linked_time(ltime) {} CLink(const std::string &call, const unsigned char *addr, time_t ltime) : callsign(call), address((const char *)addr), linked_time(ltime) {}
CLink(const CLink &from) CLink(const CLink &from)
{ {
callsign.assign(from.callsign); callsign.assign(from.callsign);
address.assign(from.address), address.assign(from.address),
linked_time=from.linked_time; linked_time=from.linked_time;
} }
CLink &operator=(const CLink &from) CLink &operator=(const CLink &from)
{ {
callsign.assign(from.callsign); callsign.assign(from.callsign);
address.assign(from.address), address.assign(from.address),
linked_time=from.linked_time; linked_time=from.linked_time;
return *this; return *this;
} }
@ -49,7 +50,8 @@ public:
time_t linked_time; time_t linked_time;
}; };
class CQnetDB { class CQnetDB
{
public: public:
CQnetDB() : db(NULL) {} CQnetDB() : db(NULL) {}
~CQnetDB() { if (db) sqlite3_close(db); } ~CQnetDB() { if (db) sqlite3_close(db); }

@ -62,7 +62,8 @@ void CQnetDVAP::calcPFCS(unsigned char *packet, unsigned char *pfcs)
{ {
unsigned short crc_dstar_ffff = 0xffff; unsigned short crc_dstar_ffff = 0xffff;
unsigned short tmp, short_c; unsigned short tmp, short_c;
unsigned short crc_tabccitt[256] = { unsigned short crc_tabccitt[256] =
{
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
@ -81,7 +82,8 @@ void CQnetDVAP::calcPFCS(unsigned char *packet, unsigned char *pfcs)
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78
}; };
for (int i = 0; i < 39 ; i++) { for (int i = 0; i < 39 ; i++)
{
short_c = 0x00ff & (unsigned short)packet[i]; short_c = 0x00ff & (unsigned short)packet[i];
tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; tmp = (crc_dstar_ffff & 0x00ff) ^ short_c;
crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp];
@ -107,12 +109,15 @@ bool CQnetDVAP::ReadConfig(const char *cfgFile)
const std::string estr; // an empty string const std::string estr; // an empty string
std::string type; std::string type;
std::string dvap_path("module_"); std::string dvap_path("module_");
if (0 > assigned_module) { if (0 > assigned_module)
{
// we need to find the lone dvap module // we need to find the lone dvap module
for (int i=0; i<3; i++) { for (int i=0; i<3; i++)
{
std::string test(dvap_path); std::string test(dvap_path);
test.append(1, 'a'+i); test.append(1, 'a'+i);
if (cfg.KeyExists(test)) { if (cfg.KeyExists(test))
{
cfg.GetValue(test, estr, type, 1, 16); cfg.GetValue(test, estr, type, 1, 16);
if (type.compare("dvap")) if (type.compare("dvap"))
continue; // this ain't it! continue; // this ain't it!
@ -121,27 +126,35 @@ bool CQnetDVAP::ReadConfig(const char *cfgFile)
break; break;
} }
} }
if (0 > assigned_module) { if (0 > assigned_module)
{
fprintf(stderr, "Error: no 'dvap' module found\n!"); fprintf(stderr, "Error: no 'dvap' module found\n!");
return true; return true;
} }
} else { }
else
{
// make sure dvap module is defined // make sure dvap module is defined
dvap_path.append(1, 'a' + assigned_module); dvap_path.append(1, 'a' + assigned_module);
if (cfg.KeyExists(dvap_path)) { if (cfg.KeyExists(dvap_path))
{
cfg.GetValue(dvap_path, estr, type, 1, 16); cfg.GetValue(dvap_path, estr, type, 1, 16);
if (type.compare("dvap")) { if (type.compare("dvap"))
{
fprintf(stderr, "%s = %s is not 'dvap' type!\n", dvap_path.c_str(), type.c_str()); fprintf(stderr, "%s = %s is not 'dvap' type!\n", dvap_path.c_str(), type.c_str());
return true; return true;
} }
} else { }
else
{
fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module); fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module);
return true; return true;
} }
} }
RPTR_MOD = 'A' + assigned_module; RPTR_MOD = 'A' + assigned_module;
cfg.GetValue("gateway_tomodem"+std::string(1, 'a'+assigned_module), estr, togate, 1, FILENAME_MAX); cfg.GetValue("gateway_tomodem"+std::string(1, 'a'+assigned_module), estr, togate, 1, FILENAME_MAX);
if (cfg.KeyExists(dvap_path+"_callsign")) { if (cfg.KeyExists(dvap_path+"_callsign"))
{
if (cfg.GetValue(dvap_path+"_callsign", type, RPTR, 3, 6)) if (cfg.GetValue(dvap_path+"_callsign", type, RPTR, 3, 6))
return true; return true;
} }
@ -150,11 +163,13 @@ bool CQnetDVAP::ReadConfig(const char *cfgFile)
if (RPTR.empty()) if (RPTR.empty())
RPTR.assign(OWNER); RPTR.assign(OWNER);
for (unsigned long i=0; i<RPTR.length(); i++) { for (unsigned long i=0; i<RPTR.length(); i++)
{
if (islower(RPTR.at(i))) if (islower(RPTR.at(i)))
RPTR.at(i) = toupper(RPTR.at(i)); RPTR.at(i) = toupper(RPTR.at(i));
} }
for (unsigned long i=0; i<OWNER.length(); i++) { for (unsigned long i=0; i<OWNER.length(); i++)
{
if (islower(OWNER.at(i))) if (islower(OWNER.at(i)))
OWNER.at(i) = toupper(OWNER.at(i)); OWNER.at(i) = toupper(OWNER.at(i));
} }
@ -209,7 +224,8 @@ void CQnetDVAP::ReadFromGateway()
bool written_to_q = false; bool written_to_q = false;
unsigned char ctrl_in = 0x80; unsigned char ctrl_in = 0x80;
while (keep_running) { while (keep_running)
{
written_to_q = false; written_to_q = false;
len = 0; len = 0;
tv.tv_sec = 0; tv.tv_sec = 0;
@ -219,15 +235,19 @@ void CQnetDVAP::ReadFromGateway()
FD_SET (fd, &readfd); FD_SET (fd, &readfd);
select(fd + 1, &readfd, NULL, NULL, &tv); select(fd + 1, &readfd, NULL, NULL, &tv);
if (FD_ISSET(fd, &readfd)) { if (FD_ISSET(fd, &readfd))
{
len = ToGate.Read(dsvt.title, 56); len = ToGate.Read(dsvt.title, 56);
if (len == 56) { if (len == 56)
if (busy20000) { {
if (busy20000)
{
FD_CLR (fd, &readfd); FD_CLR (fd, &readfd);
continue; continue;
} }
if ('G' == dsvt.hdr.rpt1[7]) { if ('G' == dsvt.hdr.rpt1[7])
{
unsigned char tmp[8]; unsigned char tmp[8];
memcpy(tmp, dsvt.hdr.rpt1, 8); memcpy(tmp, dsvt.hdr.rpt1, 8);
memcpy(dsvt.hdr.rpt1, dsvt.hdr.rpt2, 8); memcpy(dsvt.hdr.rpt1, dsvt.hdr.rpt2, 8);
@ -235,35 +255,40 @@ void CQnetDVAP::ReadFromGateway()
} }
/* check the module and gateway */ /* check the module and gateway */
if (dsvt.hdr.rpt1[7] != RPTR_MOD) { if (dsvt.hdr.rpt1[7] != RPTR_MOD)
{
FD_CLR(fd, &readfd); FD_CLR(fd, &readfd);
break; break;
} }
memcpy(dsvt.hdr.rpt2, OWNER.c_str(), 7); memcpy(dsvt.hdr.rpt2, OWNER.c_str(), 7);
dsvt.hdr.rpt2[7] = 'G'; dsvt.hdr.rpt2[7] = 'G';
if (RPTR.compare(OWNER)) { if (RPTR.compare(OWNER))
{
// restriction mode // restriction mode
memcpy(dsvt.hdr.rpt1, RPTR.c_str(), 7); memcpy(dsvt.hdr.rpt1, RPTR.c_str(), 7);
memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7); memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7);
if (memcmp(dsvt.hdr.mycall, OWNER.c_str(), 7) == 0) { if (memcmp(dsvt.hdr.mycall, OWNER.c_str(), 7) == 0)
{
/* this is an ACK back */ /* this is an ACK back */
memcpy(dsvt.hdr.mycall, RPTR.c_str(), 7); memcpy(dsvt.hdr.mycall, RPTR.c_str(), 7);
} }
} }
if ((dsvt.hdr.flag[0] != 0x00) && if ((dsvt.hdr.flag[0] != 0x00) &&
(dsvt.hdr.flag[0] != 0x01) && (dsvt.hdr.flag[0] != 0x01) &&
(dsvt.hdr.flag[0] != 0x08) && (dsvt.hdr.flag[0] != 0x08) &&
(dsvt.hdr.flag[0] != 0x20) && (dsvt.hdr.flag[0] != 0x20) &&
(dsvt.hdr.flag[0] != 0x28) && (dsvt.hdr.flag[0] != 0x28) &&
(dsvt.hdr.flag[0] != 0x40)) { (dsvt.hdr.flag[0] != 0x40))
{
FD_CLR(fd, &readfd); FD_CLR(fd, &readfd);
break; break;
} }
if (memcmp(dsvt.title, "DSVT", 4) || dsvt.id!=0x20 || dsvt.config!=0x10) { if (memcmp(dsvt.title, "DSVT", 4) || dsvt.id!=0x20 || dsvt.config!=0x10)
{
FD_CLR(fd, &readfd); FD_CLR(fd, &readfd);
break; break;
} }
@ -278,7 +303,8 @@ void CQnetDVAP::ReadFromGateway()
/* save the streamid that is winning */ /* save the streamid that is winning */
streamid = dsvt.streamid; streamid = dsvt.streamid;
if (dsvt.hdr.flag[0] != 0x01) { if (dsvt.hdr.flag[0] != 0x01)
{
if (dsvt.hdr.flag[0] == 0x00) if (dsvt.hdr.flag[0] == 0x00)
dsvt.hdr.flag[0] = 0x40; dsvt.hdr.flag[0] = 0x40;
@ -316,22 +342,33 @@ void CQnetDVAP::ReadFromGateway()
inactive = 0; inactive = 0;
seq_no = 0; seq_no = 0;
} else if (len == 27) { }
if (busy20000) { else if (len == 27)
if (dsvt.streamid == streamid) { {
if (dsvt.ctrl == ctrl_in) { if (busy20000)
{
if (dsvt.streamid == streamid)
{
if (dsvt.ctrl == ctrl_in)
{
/* do not update written_to_q, ctrl_in */ /* do not update written_to_q, ctrl_in */
; // printf("dup\n"); ; // printf("dup\n");
} else { }
else
{
ctrl_in = dsvt.ctrl; ctrl_in = dsvt.ctrl;
written_to_q = true; written_to_q = true;
if (seq_no == 0) { if (seq_no == 0)
{
dsvt.vasd.text[0] = 0x55; dsvt.vasd.text[0] = 0x55;
dsvt.vasd.text[1] = 0x2d; dsvt.vasd.text[1] = 0x2d;
dsvt.vasd.text[2] = 0x16; dsvt.vasd.text[2] = 0x16;
} else { }
if ((dsvt.vasd.text[0] == 0x55) && (dsvt.vasd.text[1] == 0x2d) && (dsvt.vasd.text[2] == 0x16)) { else
{
if ((dsvt.vasd.text[0] == 0x55) && (dsvt.vasd.text[1] == 0x2d) && (dsvt.vasd.text[2] == 0x16))
{
dsvt.vasd.text[0] = 0x70; dsvt.vasd.text[0] = 0x70;
dsvt.vasd.text[1] = 0x4f; dsvt.vasd.text[1] = 0x4f;
dsvt.vasd.text[2] = 0x93; dsvt.vasd.text[2] = 0x93;
@ -361,7 +398,8 @@ void CQnetDVAP::ReadFromGateway()
if (seq_no == 21) if (seq_no == 21)
seq_no = 0; seq_no = 0;
if ((dsvt.ctrl & 0x40) != 0) { if ((dsvt.ctrl & 0x40) != 0)
{
if (LOG_QSO) if (LOG_QSO)
printf("End G2: streamid=%04x\n", ntohs(dsvt.streamid)); printf("End G2: streamid=%04x\n", ntohs(dsvt.streamid));
@ -376,12 +414,17 @@ void CQnetDVAP::ReadFromGateway()
} }
} }
} }
} else { // busy20000 is false }
else // busy20000 is false
{
FD_CLR (fd, &readfd); FD_CLR (fd, &readfd);
break; break;
} }
} else { // len is not 56 or 27 }
if (!busy20000) { else // len is not 56 or 27
{
if (!busy20000)
{
FD_CLR (fd, &readfd); FD_CLR (fd, &readfd);
break; break;
} }
@ -391,9 +434,12 @@ void CQnetDVAP::ReadFromGateway()
// If we received a dup or select() timed out or streamids dont match, // If we received a dup or select() timed out or streamids dont match,
// then written_to_q is false // then written_to_q is false
if (!written_to_q) { // we could also end up here if we are busy and we received a non-standard packet size if (!written_to_q) // we could also end up here if we are busy and we received a non-standard packet size
if (busy20000) { {
if (++inactive >= inactiveMax) { if (busy20000)
{
if (++inactive >= inactiveMax)
{
if (LOG_QSO) if (LOG_QSO)
printf("G2 Timeout...\n"); printf("G2 Timeout...\n");
@ -404,15 +450,21 @@ void CQnetDVAP::ReadFromGateway()
busy20000 = false; busy20000 = false;
break; break;
} else { // inactive too long }
if (space == 127) { else // inactive too long
{
if (space == 127)
{
if (LOG_DEBUG) if (LOG_DEBUG)
fprintf(stderr, "sending silent frame where: len=%d, inactive=%d\n", len, inactive); fprintf(stderr, "sending silent frame where: len=%d, inactive=%d\n", len, inactive);
if (seq_no == 0) { if (seq_no == 0)
{
silence[9] = 0x55; silence[9] = 0x55;
silence[10] = 0x2d; silence[10] = 0x2d;
silence[11] = 0x16; silence[11] = 0x16;
} else { }
else
{
silence[9] = 0x70; silence[9] = 0x70;
silence[10] = 0x4f; silence[10] = 0x4f;
silence[11] = 0x93; silence[11] = 0x93;
@ -435,7 +487,8 @@ void CQnetDVAP::ReadFromGateway()
seq_no = 0; seq_no = 0;
} }
} }
} else // busy20000 is false }
else // busy20000 is false
break; break;
} }
} }
@ -481,65 +534,67 @@ void CQnetDVAP::RptrAckThread(SDVAP_ACK_ARG *parg)
// SYNC // SYNC
dr.header = 0xc012u; dr.header = 0xc012u;
dr.frame.streamid = sid; dr.frame.streamid = sid;
for (int i=0; i<10; i++) { for (int i=0; i<10; i++)
{
while ((space < 1) && keep_running) while ((space < 1) && keep_running)
usleep(5); usleep(5);
dr.frame.framepos = dr.frame.seq = i; dr.frame.framepos = dr.frame.seq = i;
switch (i) { switch (i)
case 0: {
silence[9] = 0x55; case 0:
silence[10] = 0x2d; silence[9] = 0x55;
silence[11] = 0x16; silence[10] = 0x2d;
break; silence[11] = 0x16;
case 1: break;
silence[9] = '@' ^ 0x70; case 1:
silence[10] = RADIO_ID[0] ^ 0x4f; silence[9] = '@' ^ 0x70;
silence[11] = RADIO_ID[1] ^ 0x93; silence[10] = RADIO_ID[0] ^ 0x4f;
break; silence[11] = RADIO_ID[1] ^ 0x93;
case 2: break;
silence[9] = RADIO_ID[2] ^ 0x70; case 2:
silence[10] = RADIO_ID[3] ^ 0x4f; silence[9] = RADIO_ID[2] ^ 0x70;
silence[11] = RADIO_ID[4] ^ 0x93; silence[10] = RADIO_ID[3] ^ 0x4f;
break; silence[11] = RADIO_ID[4] ^ 0x93;
case 3: break;
silence[9] = 'A' ^ 0x70; case 3:
silence[10] = RADIO_ID[5] ^ 0x4f; silence[9] = 'A' ^ 0x70;
silence[11] = RADIO_ID[6] ^ 0x93; silence[10] = RADIO_ID[5] ^ 0x4f;
break; silence[11] = RADIO_ID[6] ^ 0x93;
case 4: break;
silence[9] = RADIO_ID[7] ^ 0x70; case 4:
silence[10] = RADIO_ID[8] ^ 0x4f; silence[9] = RADIO_ID[7] ^ 0x70;
silence[11] = RADIO_ID[9] ^ 0x93; silence[10] = RADIO_ID[8] ^ 0x4f;
break; silence[11] = RADIO_ID[9] ^ 0x93;
case 5: break;
silence[9] = 'B' ^ 0x70; case 5:
silence[10] = RADIO_ID[10] ^ 0x4f; silence[9] = 'B' ^ 0x70;
silence[11] = RADIO_ID[11] ^ 0x93; silence[10] = RADIO_ID[10] ^ 0x4f;
break; silence[11] = RADIO_ID[11] ^ 0x93;
case 6: break;
silence[9] = RADIO_ID[12] ^ 0x70; case 6:
silence[10] = RADIO_ID[13] ^ 0x4f; silence[9] = RADIO_ID[12] ^ 0x70;
silence[11] = RADIO_ID[14] ^ 0x93; silence[10] = RADIO_ID[13] ^ 0x4f;
break; silence[11] = RADIO_ID[14] ^ 0x93;
case 7: break;
silence[9] = 'C' ^ 0x70; case 7:
silence[10] = RADIO_ID[15] ^ 0x4f; silence[9] = 'C' ^ 0x70;
silence[11] = RADIO_ID[16] ^ 0x93; silence[10] = RADIO_ID[15] ^ 0x4f;
break; silence[11] = RADIO_ID[16] ^ 0x93;
case 8: break;
silence[9] = RADIO_ID[17] ^ 0x70; case 8:
silence[10] = RADIO_ID[18] ^ 0x4f; silence[9] = RADIO_ID[17] ^ 0x70;
silence[11] = RADIO_ID[19] ^ 0x93; silence[10] = RADIO_ID[18] ^ 0x4f;
break; silence[11] = RADIO_ID[19] ^ 0x93;
case 9: break;
silence[0] = 0x55; case 9:
silence[1] = 0xc8; silence[0] = 0x55;
silence[2] = 0x7a; silence[1] = 0xc8;
silence[9] = 0x55; silence[2] = 0x7a;
silence[10] = 0x55; silence[9] = 0x55;
silence[11] = 0x55; silence[10] = 0x55;
dr.frame.framepos |= 0x40; silence[11] = 0x55;
break; dr.frame.framepos |= 0x40;
break;
} }
memcpy(&dr.frame.vad.voice, silence, 12); memcpy(&dr.frame.vad.voice, silence, 12);
dongle.SendRegister(dr); dongle.SendRegister(dr);
@ -568,32 +623,43 @@ void CQnetDVAP::ReadDVAPThread()
int num_dv_frames = 0; int num_dv_frames = 0;
int num_bit_errors = 0; int num_bit_errors = 0;
while (keep_running) { while (keep_running)
{
// local RF user went away ? // local RF user went away ?
if (dvap_busy) { if (dvap_busy)
{
if (last_RF_time.time() > TIMING_TIMEOUT_LOCAL_RPTR) if (last_RF_time.time() > TIMING_TIMEOUT_LOCAL_RPTR)
dvap_busy = false; dvap_busy = false;
} }
// read from the dvap and process // read from the dvap and process
reply = dongle.GetReply(dr); reply = dongle.GetReply(dr);
if (reply == RT_ERR) { if (reply == RT_ERR)
{
printf("Detected ERROR event from DVAP dongle, stopping...n"); printf("Detected ERROR event from DVAP dongle, stopping...n");
break; break;
} else if (reply == RT_STOP) { }
else if (reply == RT_STOP)
{
printf("Detected STOP event from DVAP dongle, stopping...\n"); printf("Detected STOP event from DVAP dongle, stopping...\n");
break; break;
} else if (reply == RT_START) { }
else if (reply == RT_START)
{
printf("Detected START event from DVAP dongle\n"); printf("Detected START event from DVAP dongle\n");
// else if (reply == RT_PTT) { // else if (reply == RT_PTT) {
// ptt = (dvp_buf[4] == 0x01); // ptt = (dvp_buf[4] == 0x01);
// printf("Detected PTT=%s\n", ptt?"on":"off"); // printf("Detected PTT=%s\n", ptt?"on":"off");
} else if (reply == RT_STS) { }
else if (reply == RT_STS)
{
space = (unsigned int)dr.param.sstr[2]; space = (unsigned int)dr.param.sstr[2];
if (status_cntr < 3000) if (status_cntr < 3000)
status_cntr += 20; status_cntr += 20;
} else if (reply == RT_HDR) { }
else if (reply == RT_HDR)
{
num_dv_frames = 0; num_dv_frames = 0;
num_bit_errors = 0; num_bit_errors = 0;
@ -603,12 +669,13 @@ void CQnetDVAP::ReadDVAPThread()
ok = true; ok = true;
/* Accept valid flags only */ /* Accept valid flags only */
if (ok) { if (ok)
{
if ((dr.frame.hdr.flag[0] != 0x00) && (dr.frame.hdr.flag[0] != 0x08) && // net if ((dr.frame.hdr.flag[0] != 0x00) && (dr.frame.hdr.flag[0] != 0x08) && // net
(dr.frame.hdr.flag[0] != 0x20) && (dr.frame.hdr.flag[0] != 0x28) && // flags (dr.frame.hdr.flag[0] != 0x20) && (dr.frame.hdr.flag[0] != 0x28) && // flags
(dr.frame.hdr.flag[0] != 0x40) && (dr.frame.hdr.flag[0] != 0x48) && // rptr (dr.frame.hdr.flag[0] != 0x40) && (dr.frame.hdr.flag[0] != 0x48) && // rptr
(dr.frame.hdr.flag[0] != 0x60) && (dr.frame.hdr.flag[0] != 0x68)) // flags (dr.frame.hdr.flag[0] != 0x60) && (dr.frame.hdr.flag[0] != 0x68)) // flags
ok = false; ok = false;
} }
@ -621,9 +688,9 @@ void CQnetDVAP::ReadDVAPThread()
/* RPT2 must also be valid */ /* RPT2 must also be valid */
if ((dsvt.hdr.rpt2[7] == 'A') || if ((dsvt.hdr.rpt2[7] == 'A') ||
(dsvt.hdr.rpt2[7] == 'B') || (dsvt.hdr.rpt2[7] == 'B') ||
(dsvt.hdr.rpt2[7] == 'C') || (dsvt.hdr.rpt2[7] == 'C') ||
(dsvt.hdr.rpt2[7] == 'G')) (dsvt.hdr.rpt2[7] == 'G'))
memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7); memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7);
else else
memset(dsvt.hdr.rpt2, ' ', 8); memset(dsvt.hdr.rpt2, ' ', 8);
@ -642,21 +709,28 @@ void CQnetDVAP::ReadDVAPThread()
that means that mycall, rpt1, rpt2 must be equal to RPTR that means that mycall, rpt1, rpt2 must be equal to RPTR
otherwise we drop the rf data otherwise we drop the rf data
*/ */
if (RPTR.compare(OWNER)) { if (RPTR.compare(OWNER))
if (memcmp(dsvt.hdr.mycall, RPTR.c_str(), CALL_SIZE) != 0) { {
if (memcmp(dsvt.hdr.mycall, RPTR.c_str(), CALL_SIZE) != 0)
{
printf("mycall=[%.8s], not equal to %s\n", dsvt.hdr.mycall, RPTR.c_str()); printf("mycall=[%.8s], not equal to %s\n", dsvt.hdr.mycall, RPTR.c_str());
ok = false; ok = false;
} }
} else if (memcmp(dsvt.hdr.mycall, " ", 8) == 0) { }
else if (memcmp(dsvt.hdr.mycall, " ", 8) == 0)
{
printf("Invalid value for mycall=[%.8s]\n", dsvt.hdr.mycall); printf("Invalid value for mycall=[%.8s]\n", dsvt.hdr.mycall);
ok = false; ok = false;
} }
if (ok) { if (ok)
for (i = 0; i < 8; i++) { {
for (i = 0; i < 8; i++)
{
if (!isupper(dsvt.hdr.mycall[i]) && if (!isupper(dsvt.hdr.mycall[i]) &&
!isdigit(dsvt.hdr.mycall[i]) && !isdigit(dsvt.hdr.mycall[i]) &&
(dsvt.hdr.mycall[i] != ' ')) { (dsvt.hdr.mycall[i] != ' '))
{
memset(dsvt.hdr.mycall, ' ', 8); memset(dsvt.hdr.mycall, ' ', 8);
ok = false; ok = false;
printf("Invalid value for MYCALL\n"); printf("Invalid value for MYCALL\n");
@ -664,20 +738,24 @@ void CQnetDVAP::ReadDVAPThread()
} }
} }
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++)
{
if (!isupper(dsvt.hdr.sfx[i]) && if (!isupper(dsvt.hdr.sfx[i]) &&
!isdigit(dsvt.hdr.sfx[i]) && !isdigit(dsvt.hdr.sfx[i]) &&
(dsvt.hdr.sfx[i] != ' ')) { (dsvt.hdr.sfx[i] != ' '))
{
memset(dsvt.hdr.sfx, ' ', 4); memset(dsvt.hdr.sfx, ' ', 4);
break; break;
} }
} }
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++)
{
if (!isupper(dsvt.hdr.urcall[i]) && if (!isupper(dsvt.hdr.urcall[i]) &&
!isdigit(dsvt.hdr.urcall[i]) && !isdigit(dsvt.hdr.urcall[i]) &&
(dsvt.hdr.urcall[i] != ' ') && (dsvt.hdr.urcall[i] != ' ') &&
(dsvt.hdr.urcall[i] != '/')) { (dsvt.hdr.urcall[i] != '/'))
{
memcpy(dsvt.hdr.urcall, "CQCQCQ ", 8); memcpy(dsvt.hdr.urcall, "CQCQCQ ", 8);
break; break;
} }
@ -724,9 +802,12 @@ void CQnetDVAP::ReadDVAPThread()
memcpy(mycall, dr.frame.hdr.mycall, 8); memcpy(mycall, dr.frame.hdr.mycall, 8);
} }
} else if (reply == RT_DAT) { }
else if (reply == RT_DAT)
{
/* have we already received a header ? */ /* have we already received a header ? */
if (dvap_busy) { if (dvap_busy)
{
the_end = ((dr.frame.framepos & 0x40) == 0x40); the_end = ((dr.frame.framepos & 0x40) == 0x40);
dsvt.config = 0x20U; dsvt.config = 0x20U;
@ -738,7 +819,8 @@ void CQnetDVAP::ReadDVAPThread()
int ber_data[3]; int ber_data[3];
int ber_errs = decode.Decode(dsvt.vasd.voice, ber_data); int ber_errs = decode.Decode(dsvt.vasd.voice, ber_data);
if (ber_data[0] != 0xf85) { if (ber_data[0] != 0xf85)
{
num_bit_errors += ber_errs; num_bit_errors += ber_errs;
num_dv_frames++; num_dv_frames++;
} }
@ -748,7 +830,8 @@ void CQnetDVAP::ReadDVAPThread()
// local RF user still talking, update timer // local RF user still talking, update timer
last_RF_time.start(); last_RF_time.start();
if (the_end) { if (the_end)
{
// local RF user stopped talking // local RF user stopped talking
dvap_busy = false; dvap_busy = false;
SDVAP_ACK_ARG dvap_ack_arg; SDVAP_ACK_ARG dvap_ack_arg;
@ -756,11 +839,15 @@ void CQnetDVAP::ReadDVAPThread()
if (LOG_QSO) if (LOG_QSO)
printf("End of dvap audio, ber=%.02f\n", dvap_ack_arg.ber); printf("End of dvap audio, ber=%.02f\n", dvap_ack_arg.ber);
if (MODULE_ACKNOWLEDGE && !busy20000) { if (MODULE_ACKNOWLEDGE && !busy20000)
{
memcpy(dvap_ack_arg.mycall, mycall, 8); memcpy(dvap_ack_arg.mycall, mycall, 8);
try { try
{
std::async(std::launch::async, &CQnetDVAP::RptrAckThread, this, &dvap_ack_arg); std::async(std::launch::async, &CQnetDVAP::RptrAckThread, this, &dvap_ack_arg);
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
printf("Failed to start RptrAckThread(). Exception: %s\n", e.what()); printf("Failed to start RptrAckThread(). Exception: %s\n", e.what());
} }
} }
@ -787,16 +874,19 @@ bool CQnetDVAP::Init(const char *file, const int amod)
{ {
assigned_module = amod; assigned_module = amod;
if (ReadConfig(file)) { if (ReadConfig(file))
{
printf("Failed to process config file %s\n", file); printf("Failed to process config file %s\n", file);
return true; return true;
} }
if (RPTR.length() != 8) { if (RPTR.length() != 8)
{
printf("Bad RPTR value, length must be exactly 8 bytes\n"); printf("Bad RPTR value, length must be exactly 8 bytes\n");
return true; return true;
} }
if ((RPTR_MOD != 'A') && (RPTR_MOD != 'B') && (RPTR_MOD != 'C')) { if ((RPTR_MOD != 'A') && (RPTR_MOD != 'B') && (RPTR_MOD != 'C'))
{
printf("Bad RPTR_MOD value, must be one of A or B or C\n"); printf("Bad RPTR_MOD value, must be one of A or B or C\n");
return true; return true;
} }
@ -818,7 +908,8 @@ bool CQnetDVAP::Init(const char *file, const int amod)
if (!dongle.Initialize(MODULE_SERIAL_NUMBER.c_str(), MODULE_FREQUENCY, MODULE_OFFSET, MODULE_POWER, MODULE_SQUELCH)) if (!dongle.Initialize(MODULE_SERIAL_NUMBER.c_str(), MODULE_FREQUENCY, MODULE_OFFSET, MODULE_POWER, MODULE_SQUELCH))
return true; return true;
if (ToGate.Open(togate.c_str(), this)) { if (ToGate.Open(togate.c_str(), this))
{
dongle.Stop(); dongle.Stop();
close(serfd); close(serfd);
return true; return true;
@ -831,25 +922,33 @@ void CQnetDVAP::Run()
{ {
CTimer ackpoint; CTimer ackpoint;
std::future<void> readthread; std::future<void> readthread;
try { try
{
readthread = std::async(std::launch::async, &CQnetDVAP::ReadDVAPThread, this); readthread = std::async(std::launch::async, &CQnetDVAP::ReadDVAPThread, this);
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
printf("Unable to start ReadDVAPThread(). Exception: %s\n", e.what()); printf("Unable to start ReadDVAPThread(). Exception: %s\n", e.what());
keep_running = false; keep_running = false;
} }
printf("Started ReadDVAPThread()\n"); printf("Started ReadDVAPThread()\n");
int cnt = 0; int cnt = 0;
while (keep_running) { while (keep_running)
if (ackpoint.time() > 2.5) { {
if (ackpoint.time() > 2.5)
{
int rc = dongle.KeepAlive(); int rc = dongle.KeepAlive();
if (rc < 0) { if (rc < 0)
{
cnt ++; cnt ++;
if (cnt > 5) { if (cnt > 5)
{
printf("Could not send KEEPALIVE signal to dvap 5 times...exiting\n"); printf("Could not send KEEPALIVE signal to dvap 5 times...exiting\n");
keep_running = false; keep_running = false;
} }
} else }
else
cnt = 0; cnt = 0;
ackpoint.start(); ackpoint.start();
} }
@ -867,12 +966,14 @@ int main(int argc, char *argv[])
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
printf("dvap_rptr VERSION %s\n", DVAP_VERSION); printf("dvap_rptr VERSION %s\n", DVAP_VERSION);
if (argc != 2) { if (argc != 2)
{
fprintf(stderr, "Usage: %s dvap_rptr.cfg\n", argv[0]); fprintf(stderr, "Usage: %s dvap_rptr.cfg\n", argv[0]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if ('-' == argv[1][0]) { if ('-' == argv[1][0])
{
printf("\nQnetDVAP Version #%s Copyright (C) 2018-2020 by Thomas A. Early N7TAE\n", DVAP_VERSION); printf("\nQnetDVAP Version #%s Copyright (C) 2018-2020 by Thomas A. Early N7TAE\n", DVAP_VERSION);
printf("QnetDVAP comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("QnetDVAP comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n");
@ -880,29 +981,31 @@ int main(int argc, char *argv[])
} }
const char *qn = strstr(argv[0], "qndvap"); const char *qn = strstr(argv[0], "qndvap");
if (NULL == qn) { if (NULL == qn)
{
fprintf(stderr, "Error finding 'qndvap' in %s!\n", argv[0]); fprintf(stderr, "Error finding 'qndvap' in %s!\n", argv[0]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
qn += 6; qn += 6;
int mod; int mod;
switch (*qn) { switch (*qn)
case NULL: {
mod = -1; case NULL:
break; mod = -1;
case 'a': break;
mod = 0; case 'a':
break; mod = 0;
case 'b': break;
mod = 1; case 'b':
break; mod = 1;
case 'c': break;
mod = 2; case 'c':
break; mod = 2;
default: break;
fprintf(stderr, "ERROR: '%s' is not a valid module\nassigned module must be a, b or c\n", argv[1]); default:
return 1; fprintf(stderr, "ERROR: '%s' is not a valid module\nassigned module must be a, b or c\n", argv[1]);
return 1;
} }
CQnetDVAP dvap; CQnetDVAP dvap;

@ -20,7 +20,8 @@
#include "KRBase.h" #include "KRBase.h"
using SDVAP_ACK_ARG = struct davp_ack_arg_tag { using SDVAP_ACK_ARG = struct davp_ack_arg_tag
{
char mycall[8]; char mycall[8];
float ber; float ber;
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -35,20 +35,23 @@
#define CALL_SIZE 8 #define CALL_SIZE 8
#define MAX_DTMF_BUF 32 #define MAX_DTMF_BUF 32
using STOREMOTEG2 = struct to_remote_g2_tag { using STOREMOTEG2 = struct to_remote_g2_tag
{
unsigned short streamid; unsigned short streamid;
CSockAddress toDstar; CSockAddress toDstar;
time_t last_time; time_t last_time;
}; };
using STOREPEATER = struct torepeater_tag { using STOREPEATER = struct torepeater_tag
{
// help with header re-generation // help with header re-generation
SDSVT saved_hdr; // repeater format SDSVT saved_hdr; // repeater format
time_t last_time; time_t last_time;
unsigned char sequence; unsigned char sequence;
}; };
using SBANDTXT = struct band_txt_tag { using SBANDTXT = struct band_txt_tag
{
unsigned short streamID; unsigned short streamID;
unsigned char flags[3]; unsigned char flags[3];
std::string mycall, sfx, urcall, rpt1, rpt2, txt, dest_rptr; std::string mycall, sfx, urcall, rpt1, rpt2, txt, dest_rptr;
@ -76,7 +79,8 @@ using SBANDTXT = struct band_txt_tag {
} }
}; };
using SSD = struct sd_tag { using SSD = struct sd_tag
{
unsigned char header[41]; unsigned char header[41];
unsigned char message[21]; unsigned char message[21];
unsigned char gps[256]; unsigned char gps[256];
@ -96,12 +100,12 @@ public:
bool Init(char *cfgfile); bool Init(char *cfgfile);
private: private:
// link type // link type
int link_family[3] = { AF_UNSPEC, AF_UNSPEC, AF_UNSPEC }; int link_family[3] = { AF_UNSPEC, AF_UNSPEC, AF_UNSPEC };
// network type // network type
int af_family[2] = { AF_UNSPEC, AF_UNSPEC }; int af_family[2] = { AF_UNSPEC, AF_UNSPEC };
int Index[3] = { -1, -1, -1 }; int Index[3] = { -1, -1, -1 };
SPORTIP g2_external, g2_ipv6_external, ircddb[2]; SPORTIP g2_external, g2_ipv6_external, ircddb[2];
@ -197,7 +201,7 @@ private:
bool Flag_is_ok(unsigned char flag); bool Flag_is_ok(unsigned char flag);
void UnpackCallsigns(const std::string &str, std::set<std::string> &set, const std::string &delimiters = ","); void UnpackCallsigns(const std::string &str, std::set<std::string> &set, const std::string &delimiters = ",");
void PrintCallsigns(const std::string &key, const std::set<std::string> &set); void PrintCallsigns(const std::string &key, const std::set<std::string> &set);
int FindIndex(const int i) const; int FindIndex(const int i) const;
// read configuration file // read configuration file
bool ReadConfig(char *); bool ReadConfig(char *);

@ -48,7 +48,7 @@
#define ITAP_VERSION "QnetITAP-526" #define ITAP_VERSION "QnetITAP-526"
CQnetITAP::CQnetITAP(int mod) CQnetITAP::CQnetITAP(int mod)
: assigned_module(mod) : assigned_module(mod)
{ {
} }
@ -70,19 +70,22 @@ bool CQnetITAP::Initialize(const char *cfgfile)
int CQnetITAP::OpenITAP() int CQnetITAP::OpenITAP()
{ {
int fd = open(ITAP_DEVICE.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); int fd = open(ITAP_DEVICE.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
if (fd < 0) { if (fd < 0)
{
printf("Failed to open device [%s], error=%d, message=%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno)); printf("Failed to open device [%s], error=%d, message=%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno));
return -1; return -1;
} }
if (isatty(fd) == 0) { if (isatty(fd) == 0)
{
printf("Device %s is not a tty device\n", ITAP_DEVICE.c_str()); printf("Device %s is not a tty device\n", ITAP_DEVICE.c_str());
close(fd); close(fd);
return -1; return -1;
} }
static termios t; static termios t;
if (tcgetattr(fd, &t) < 0) { if (tcgetattr(fd, &t) < 0)
{
printf("tcgetattr failed for %s, error=%d, message-%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno)); printf("tcgetattr failed for %s, error=%d, message-%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -99,7 +102,8 @@ int CQnetITAP::OpenITAP()
cfsetospeed(&t, B38400); cfsetospeed(&t, B38400);
cfsetispeed(&t, B38400); cfsetispeed(&t, B38400);
if (tcsetattr(fd, TCSANOW, &t) < 0) { if (tcsetattr(fd, TCSANOW, &t) < 0)
{
printf("tcsetattr failed for %s, error=%dm message=%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno)); printf("tcsetattr failed for %s, error=%dm message=%s\n", ITAP_DEVICE.c_str(), errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -111,35 +115,37 @@ int CQnetITAP::OpenITAP()
void CQnetITAP::DumpSerialPacket(const char *title, const unsigned char *buf) void CQnetITAP::DumpSerialPacket(const char *title, const unsigned char *buf)
{ {
printf("%s: ", title); printf("%s: ", title);
if (buf[0] > 41) { if (buf[0] > 41)
{
printf("UNKNOWN: length=%u", (unsigned)buf[0]); printf("UNKNOWN: length=%u", (unsigned)buf[0]);
return; return;
} }
SITAP itap; SITAP itap;
memcpy(&itap, buf, buf[0]); memcpy(&itap, buf, buf[0]);
switch (itap.type) { switch (itap.type)
case 0x03U: //pong {
printf("Pong\n"); case 0x03U: //pong
break; printf("Pong\n");
case 0x10U: // header break;
case 0x20U: case 0x10U: // header
printf("Header ur=%8.8s r1=%8.8s r2=%8.8s my=%8.8s/%4.4s", itap.header.ur, itap.header.r1, itap.header.r2, itap.header.my, itap.header.nm); case 0x20U:
break; printf("Header ur=%8.8s r1=%8.8s r2=%8.8s my=%8.8s/%4.4s", itap.header.ur, itap.header.r1, itap.header.r2, itap.header.my, itap.header.nm);
case 0x12U: // data break;
case 0x22U: case 0x12U: // data
printf("Data count=%u seq=%u f=%02u%02u%02u a=%02u%02u%02u%02u%02u%02u%02u%02u%02u t=%02u%02u%02u\n", itap.voice.counter, itap.voice.sequence, itap.header.flag[0], itap.header.flag[1], itap.header.flag[2], itap.voice.ambe[0], itap.voice.ambe[1], itap.voice.ambe[2], itap.voice.ambe[3], itap.voice.ambe[4], itap.voice.ambe[5], itap.voice.ambe[6], itap.voice.ambe[7], itap.voice.ambe[8], itap.voice.text[0], itap.voice.text[1], itap.voice.text[2]); case 0x22U:
break; printf("Data count=%u seq=%u f=%02u%02u%02u a=%02u%02u%02u%02u%02u%02u%02u%02u%02u t=%02u%02u%02u\n", itap.voice.counter, itap.voice.sequence, itap.header.flag[0], itap.header.flag[1], itap.header.flag[2], itap.voice.ambe[0], itap.voice.ambe[1], itap.voice.ambe[2], itap.voice.ambe[3], itap.voice.ambe[4], itap.voice.ambe[5], itap.voice.ambe[6], itap.voice.ambe[7], itap.voice.ambe[8], itap.voice.text[0], itap.voice.text[1], itap.voice.text[2]);
case 0x11U: // header acknowledgement break;
case 0x21U: case 0x11U: // header acknowledgement
printf("Header acknowledgement code=%02u", itap.header.flag[0]); case 0x21U:
break; printf("Header acknowledgement code=%02u", itap.header.flag[0]);
case 0x13U: // data acknowledgment break;
case 0x23U: case 0x13U: // data acknowledgment
printf("Data acknowledgement seq=%02u code=%02u", itap.header.flag[0], itap.header.flag[1]); case 0x23U:
break; printf("Data acknowledgement seq=%02u code=%02u", itap.header.flag[0], itap.header.flag[1]);
default: break;
printf("UNKNOWN packet buf[0] = 0x%02u\n", buf[0]); default:
break; printf("UNKNOWN packet buf[0] = 0x%02u\n", buf[0]);
break;
} }
} }
@ -150,7 +156,8 @@ REPLY_TYPE CQnetITAP::GetITAPData(unsigned char *buf)
// Get the buffer size or nothing at all // Get the buffer size or nothing at all
int ret = read(serfd, buf, 1U); int ret = read(serfd, buf, 1U);
if (ret < 0) { if (ret < 0)
{
printf("Error when reading first byte from the Icom radio %d: %s", errno, strerror(errno)); printf("Error when reading first byte from the Icom radio %d: %s", errno, strerror(errno));
return RT_ERROR; return RT_ERROR;
} }
@ -163,17 +170,20 @@ REPLY_TYPE CQnetITAP::GetITAPData(unsigned char *buf)
unsigned int length = buf[0U]; unsigned int length = buf[0U];
if (length >= 100U) { if (length >= 100U)
{
printf("Invalid data received from the Icom radio, length=%d\n", length); printf("Invalid data received from the Icom radio, length=%d\n", length);
return RT_ERROR; return RT_ERROR;
} }
unsigned int offset = 1U; unsigned int offset = 1U;
while (offset < length) { while (offset < length)
{
ret = read(serfd, buf + offset, length - offset); ret = read(serfd, buf + offset, length - offset);
if (ret<0 && errno!=EAGAIN) { if (ret<0 && errno!=EAGAIN)
{
printf("Error when reading buffer from the Icom radio %d: %s\n", errno, strerror(errno)); printf("Error when reading buffer from the Icom radio %d: %s\n", errno, strerror(errno));
return RT_ERROR; return RT_ERROR;
} }
@ -182,19 +192,20 @@ REPLY_TYPE CQnetITAP::GetITAPData(unsigned char *buf)
offset += ret; offset += ret;
} }
switch (buf[1U]) { switch (buf[1U])
case 0x03U: {
return RT_PONG; case 0x03U:
case 0x10U: return RT_PONG;
return RT_HEADER; case 0x10U:
case 0x12U: return RT_HEADER;
return RT_DATA; case 0x12U:
case 0x21U: return RT_DATA;
return RT_HEADER_ACK; case 0x21U:
case 0x23U: return RT_HEADER_ACK;
return RT_DATA_ACK; case 0x23U:
default: return RT_DATA_ACK;
return RT_UNKNOWN; default:
return RT_UNKNOWN;
} }
} }
@ -220,7 +231,8 @@ void CQnetITAP::Run(const char *cfgfile)
double pingtime = 0.001; double pingtime = 0.001;
const double ackwait = AP_MODE ? 0.4 : 0.06; const double ackwait = AP_MODE ? 0.4 : 0.06;
while (keep_running) { while (keep_running)
{
fd_set readfds; fd_set readfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
@ -235,24 +247,30 @@ void CQnetITAP::Run(const char *cfgfile)
// don't care about writefds and exceptfds: // don't care about writefds and exceptfds:
// and we'll wait for 5 ms // and we'll wait for 5 ms
int ret = select(maxfs+1, &readfds, NULL, NULL, &tv); int ret = select(maxfs+1, &readfds, NULL, NULL, &tv);
if (ret < 0) { if (ret < 0)
{
printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno)); printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno));
break; break;
} }
// check for a dead or disconnected radio // check for a dead or disconnected radio
if (10.0 < lastdataTimer.time()) { if (10.0 < lastdataTimer.time())
{
printf("no activity from radio for 10 sec. Exiting...\n"); printf("no activity from radio for 10 sec. Exiting...\n");
break; break;
} }
if (pingTimer.time() >= pingtime) { if (pingTimer.time() >= pingtime)
if (poll_counter < 18 ) { {
if (poll_counter < 18 )
{
const unsigned char poll[2] = { 0xffu, 0xffu }; const unsigned char poll[2] = { 0xffu, 0xffu };
SendTo(poll); SendTo(poll);
if (poll_counter == 17) if (poll_counter == 17)
pingtime = 1.0; pingtime = 1.0;
} else { }
else
{
const unsigned char ping[2] = { 0x02u, 0x02u }; const unsigned char ping[2] = { 0x02u, 0x02u };
SendTo(ping); SendTo(ping);
} }
@ -262,79 +280,93 @@ void CQnetITAP::Run(const char *cfgfile)
unsigned char buf[100]; unsigned char buf[100];
if (keep_running && FD_ISSET(serfd, &readfds)) { // there is something to read! if (keep_running && FD_ISSET(serfd, &readfds)) // there is something to read!
switch (GetITAPData(buf)) { {
case RT_ERROR: switch (GetITAPData(buf))
keep_running = false; {
break; case RT_ERROR:
case RT_HEADER: keep_running = false;
{ break;
unsigned char ack_header[3] = { 0x03U, 0x11U, 0x0U }; case RT_HEADER:
SendTo(ack_header); {
} unsigned char ack_header[3] = { 0x03U, 0x11U, 0x0U };
if (ProcessITAP(buf)) SendTo(ack_header);
keep_running = false; }
lastdataTimer.start(); if (ProcessITAP(buf))
break; keep_running = false;
case RT_DATA: lastdataTimer.start();
break;
case RT_DATA:
{
unsigned char ack_voice[4] = { 0x04U, 0x13U, 0x0U, 0x0U };
ack_voice[2] = buf[2];
SendTo(ack_voice);
}
if (ProcessITAP(buf))
keep_running = false;
lastdataTimer.start();
break;
case RT_PONG:
if (! is_alive)
{
if (LOG_DEBUG)
{ {
unsigned char ack_voice[4] = { 0x04U, 0x13U, 0x0U, 0x0U }; auto count = queue.size();
ack_voice[2] = buf[2]; if (count)
SendTo(ack_voice); printf("%u packets in queue. Icom radio is connected.", (unsigned int)count);
}
if (ProcessITAP(buf))
keep_running = false;
lastdataTimer.start();
break;
case RT_PONG:
if (! is_alive) {
if (LOG_DEBUG) {
auto count = queue.size();
if (count)
printf("%u packets in queue. Icom radio is connected.", (unsigned int)count);
} else
printf("Icom Radio is connected.\n");
is_alive = true;
}
lastdataTimer.start();
break;
case RT_HEADER_ACK:
if (acknowledged) {
fprintf(stderr, "ERROR: Header already acknowledged!\n");
} else {
if (0x0U == buf[2])
acknowledged = true;
}
lastdataTimer.start();
break;
case RT_DATA_ACK:
if (acknowledged) {
fprintf(stderr, "ERROR: voice frame %d already acknowledged!\n", (int)buf[2]);
} else {
if (0x0U == buf[3])
acknowledged = true;
} }
lastdataTimer.start(); else
break; printf("Icom Radio is connected.\n");
case RT_TIMEOUT: // nothing or 0xff is_alive = true;
break; }
default: lastdataTimer.start();
if (buf[0] != 255) break;
DumpSerialPacket("GetITAPData returned", buf); case RT_HEADER_ACK:
break; if (acknowledged)
{
fprintf(stderr, "ERROR: Header already acknowledged!\n");
}
else
{
if (0x0U == buf[2])
acknowledged = true;
}
lastdataTimer.start();
break;
case RT_DATA_ACK:
if (acknowledged)
{
fprintf(stderr, "ERROR: voice frame %d already acknowledged!\n", (int)buf[2]);
}
else
{
if (0x0U == buf[3])
acknowledged = true;
}
lastdataTimer.start();
break;
case RT_TIMEOUT: // nothing or 0xff
break;
default:
if (buf[0] != 255)
DumpSerialPacket("GetITAPData returned", buf);
break;
} }
FD_CLR(serfd, &readfds); FD_CLR(serfd, &readfds);
} }
if (keep_running && FD_ISSET(ug2m, &readfds)) { if (keep_running && FD_ISSET(ug2m, &readfds))
{
ssize_t len = ToGate.Read(buf, 100); ssize_t len = ToGate.Read(buf, 100);
if (len < 0) { if (len < 0)
{
printf("ERROR: Run: recvfrom(gsock) returned error %d, %s\n", errno, strerror(errno)); printf("ERROR: Run: recvfrom(gsock) returned error %d, %s\n", errno, strerror(errno));
break; break;
} }
if (0 == memcmp(buf, "DSVT", 4)) { if (0 == memcmp(buf, "DSVT", 4))
{
//printf("read %d bytes from QnetGateway\n", (int)len); //printf("read %d bytes from QnetGateway\n", (int)len);
if (ProcessGateway(len, buf)) if (ProcessGateway(len, buf))
break; break;
@ -343,10 +375,14 @@ void CQnetITAP::Run(const char *cfgfile)
} }
// send queued frames // send queued frames
if (keep_running) { if (keep_running)
if (acknowledged) { {
if (is_alive) { if (acknowledged)
if (! queue.empty()) { {
if (is_alive)
{
if (! queue.empty())
{
CFrame frame = queue.front(); CFrame frame = queue.front();
queue.pop(); queue.pop();
SendTo(frame.data()); SendTo(frame.data());
@ -354,8 +390,11 @@ void CQnetITAP::Run(const char *cfgfile)
acknowledged = false; acknowledged = false;
} }
} }
} else { // we are waiting on an acknowledgement }
if (ackTimer.time() >= ackwait) { else // we are waiting on an acknowledgement
{
if (ackTimer.time() >= ackwait)
{
fprintf(stderr, "Icom failure suspected, restarting...\n"); fprintf(stderr, "Icom failure suspected, restarting...\n");
close(serfd); close(serfd);
poll_counter = 0; poll_counter = 0;
@ -365,9 +404,12 @@ void CQnetITAP::Run(const char *cfgfile)
lastdataTimer.start(); lastdataTimer.start();
pingTimer.start(); pingTimer.start();
serfd = OpenITAP(); serfd = OpenITAP();
if (serfd < 0) { if (serfd < 0)
{
keep_running = false; keep_running = false;
} else { }
else
{
while (! queue.empty()) while (! queue.empty())
queue.pop(); queue.pop();
} }
@ -386,10 +428,13 @@ int CQnetITAP::SendTo(const unsigned char *buf)
unsigned int ptr = 0; unsigned int ptr = 0;
unsigned int length = (0xffu == buf[0]) ? 2 : buf[0]; unsigned int length = (0xffu == buf[0]) ? 2 : buf[0];
while (ptr < length) { while (ptr < length)
{
n = write(serfd, buf + ptr, length - ptr); n = write(serfd, buf + ptr, length - ptr);
if (n < 0) { if (n < 0)
if (EAGAIN != errno) { {
if (EAGAIN != errno)
{
printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno)); printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1; return -1;
} }
@ -398,11 +443,14 @@ int CQnetITAP::SendTo(const unsigned char *buf)
} }
n = 0; // send an ending 0xffu n = 0; // send an ending 0xffu
while (0 == n) { while (0 == n)
{
const unsigned char push = 0xffu; const unsigned char push = 0xffu;
n = write(serfd, &push, 1); n = write(serfd, &push, 1);
if (n < 0) { if (n < 0)
if (EAGAIN != errno) { {
if (EAGAIN != errno)
{
printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno)); printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1; return -1;
} }
@ -415,20 +463,25 @@ int CQnetITAP::SendTo(const unsigned char *buf)
bool CQnetITAP::ProcessGateway(const int len, const unsigned char *raw) bool CQnetITAP::ProcessGateway(const int len, const unsigned char *raw)
{ {
static unsigned char counter = 0; static unsigned char counter = 0;
if (27==len || 56==len) { //here is dstar data if (27==len || 56==len) //here is dstar data
{
SDSVT dsvt; SDSVT dsvt;
memcpy(dsvt.title, raw, len); // transfer raw data to SDSVT struct memcpy(dsvt.title, raw, len); // transfer raw data to SDSVT struct
SITAP itap; // destination SITAP itap; // destination
if (56 == len) { // write a Header packet if (56 == len) // write a Header packet
{
counter = 0; counter = 0;
itap.length = 41U; itap.length = 41U;
itap.type = 0x20; itap.type = 0x20;
memcpy(itap.header.flag, dsvt.hdr.flag, 3); memcpy(itap.header.flag, dsvt.hdr.flag, 3);
if (RPTR_MOD == dsvt.hdr.rpt2[7]) { if (RPTR_MOD == dsvt.hdr.rpt2[7])
{
memcpy(itap.header.r1, dsvt.hdr.rpt2, 8); memcpy(itap.header.r1, dsvt.hdr.rpt2, 8);
memcpy(itap.header.r2, dsvt.hdr.rpt1, 8); memcpy(itap.header.r2, dsvt.hdr.rpt1, 8);
} else { }
else
{
memcpy(itap.header.r1, dsvt.hdr.rpt1, 8); memcpy(itap.header.r1, dsvt.hdr.rpt1, 8);
memcpy(itap.header.r2, dsvt.hdr.rpt2, 8); memcpy(itap.header.r2, dsvt.hdr.rpt2, 8);
} }
@ -437,7 +490,9 @@ bool CQnetITAP::ProcessGateway(const int len, const unsigned char *raw)
memcpy(itap.header.nm, dsvt.hdr.sfx, 4); memcpy(itap.header.nm, dsvt.hdr.sfx, 4);
if (LOG_QSO) if (LOG_QSO)
printf("Queued ITAP to %s ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ITAP_DEVICE.c_str(), itap.header.ur, itap.header.r1, itap.header.r2, itap.header.my, itap.header.nm); printf("Queued ITAP to %s ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ITAP_DEVICE.c_str(), itap.header.ur, itap.header.r1, itap.header.r2, itap.header.my, itap.header.nm);
} else { // write an AMBE packet }
else // write an AMBE packet
{
itap.length = 16U; itap.length = 16U;
itap.type = 0x22U; itap.type = 0x22U;
itap.voice.counter = counter++; itap.voice.counter = counter++;
@ -450,7 +505,8 @@ bool CQnetITAP::ProcessGateway(const int len, const unsigned char *raw)
} }
queue.push(CFrame(&itap.length)); queue.push(CFrame(&itap.length));
} else }
else
printf("DEBUG: ProcessGateway: unusual packet size read len=%d\n", len); printf("DEBUG: ProcessGateway: unusual packet size read len=%d\n", len);
return false; return false;
} }
@ -477,19 +533,22 @@ bool CQnetITAP::ProcessITAP(const unsigned char *buf)
dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1 : (('C'==RPTR_MOD) ? 0x2 : 0x3); dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1 : (('C'==RPTR_MOD) ? 0x2 : 0x3);
dsvt.streamid = htons(stream_id); dsvt.streamid = htons(stream_id);
if (41 == len) { // header if (41 == len) // header
{
dsvt.ctrl = 0x80; dsvt.ctrl = 0x80;
memcpy(dsvt.hdr.flag, itap.header.flag, 3); memcpy(dsvt.hdr.flag, itap.header.flag, 3);
////////////////// Terminal or Access ///////////////////////// ////////////////// Terminal or Access /////////////////////////
if (0 == memcmp(itap.header.r1, "DIRECT", 6)) { if (0 == memcmp(itap.header.r1, "DIRECT", 6))
{
// Terminal Mode! // Terminal Mode!
memcpy(dsvt.hdr.rpt1, RPTR.c_str(), 7); // build r1 memcpy(dsvt.hdr.rpt1, RPTR.c_str(), 7); // build r1
dsvt.hdr.rpt1[7] = RPTR_MOD; // with module dsvt.hdr.rpt1[7] = RPTR_MOD; // with module
memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7); // build r2 memcpy(dsvt.hdr.rpt2, RPTR.c_str(), 7); // build r2
dsvt.hdr.rpt2[7] = 'G'; // with gateway dsvt.hdr.rpt2[7] = 'G'; // with gateway
if (' '==itap.header.ur[2] && ' '!=itap.header.ur[0]) { if (' '==itap.header.ur[2] && ' '!=itap.header.ur[0])
{
// it's a command because it has as space in the 3rd position, we have to right-justify it! // it's a command because it has as space in the 3rd position, we have to right-justify it!
// Terminal Mode left justifies short commands. // Terminal Mode left justifies short commands.
memset(dsvt.hdr.urcall, ' ', 8); // first file ur with spaces memset(dsvt.hdr.urcall, ' ', 8); // first file ur with spaces
@ -497,9 +556,12 @@ bool CQnetITAP::ProcessITAP(const unsigned char *buf)
dsvt.hdr.urcall[7] = itap.header.ur[0]; // one char command, like "E" or "I" dsvt.hdr.urcall[7] = itap.header.ur[0]; // one char command, like "E" or "I"
else else
memcpy(dsvt.hdr.urcall+6, itap.header.ur, 2); // two char command, like "HX" or "S0" memcpy(dsvt.hdr.urcall+6, itap.header.ur, 2); // two char command, like "HX" or "S0"
} else }
else
memcpy(dsvt.hdr.urcall, itap.header.ur, 8); // ur is at least 3 chars memcpy(dsvt.hdr.urcall, itap.header.ur, 8); // ur is at least 3 chars
} else { }
else
{
// Access Point Mode // Access Point Mode
memcpy(dsvt.hdr.rpt1, itap.header.r1, 8); memcpy(dsvt.hdr.rpt1, itap.header.r1, 8);
memcpy(dsvt.hdr.rpt2, itap.header.r2, 8); memcpy(dsvt.hdr.rpt2, itap.header.r2, 8);
@ -510,16 +572,20 @@ bool CQnetITAP::ProcessITAP(const unsigned char *buf)
memcpy(dsvt.hdr.mycall, itap.header.my, 8); memcpy(dsvt.hdr.mycall, itap.header.my, 8);
memcpy(dsvt.hdr.sfx, itap.header.nm, 4); memcpy(dsvt.hdr.sfx, itap.header.nm, 4);
calcPFCS(dsvt.hdr.flag, dsvt.hdr.pfcs); calcPFCS(dsvt.hdr.flag, dsvt.hdr.pfcs);
if (ToGate.Write(dsvt.title, 56)) { if (ToGate.Write(dsvt.title, 56))
{
printf("ERROR: ProcessITAP: Could not write gateway header packet\n"); printf("ERROR: ProcessITAP: Could not write gateway header packet\n");
return true; return true;
} }
if (LOG_QSO) if (LOG_QSO)
printf("Sent DSVT to gateway, streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx); printf("Sent DSVT to gateway, streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx);
} else if (16 == len) { // ambe }
else if (16 == len) // ambe
{
dsvt.ctrl = itap.voice.sequence; dsvt.ctrl = itap.voice.sequence;
memcpy(dsvt.vasd.voice, itap.voice.ambe, 12); memcpy(dsvt.vasd.voice, itap.voice.ambe, 12);
if (ToGate.Write(dsvt.title, 27)) { if (ToGate.Write(dsvt.title, 27))
{
printf("ERROR: ProcessMMDVM: Could not write gateway voice packet\n"); printf("ERROR: ProcessMMDVM: Could not write gateway voice packet\n");
return true; return true;
} }
@ -542,12 +608,15 @@ bool CQnetITAP::ReadConfig(const char *cfgFile)
const std::string estr; // an empty string const std::string estr; // an empty string
std::string type; std::string type;
std::string itap_path("module_"); std::string itap_path("module_");
if (0 > assigned_module) { if (0 > assigned_module)
{
// we need to find the lone itap module // we need to find the lone itap module
for (int i=0; i<3; i++) { for (int i=0; i<3; i++)
{
std::string test(itap_path); std::string test(itap_path);
test.append(1, 'a'+i); test.append(1, 'a'+i);
if (cfg.KeyExists(test)) { if (cfg.KeyExists(test))
{
cfg.GetValue(test, estr, type, 1, 16); cfg.GetValue(test, estr, type, 1, 16);
if (type.compare("itap")) if (type.compare("itap"))
continue; // this ain't it! continue; // this ain't it!
@ -556,20 +625,27 @@ bool CQnetITAP::ReadConfig(const char *cfgFile)
break; break;
} }
} }
if (0 > assigned_module) { if (0 > assigned_module)
{
fprintf(stderr, "Error: no 'itap' module found\n!"); fprintf(stderr, "Error: no 'itap' module found\n!");
return true; return true;
} }
} else { }
else
{
// make sure itap module is defined // make sure itap module is defined
itap_path.append(1, 'a' + assigned_module); itap_path.append(1, 'a' + assigned_module);
if (cfg.KeyExists(itap_path)) { if (cfg.KeyExists(itap_path))
{
cfg.GetValue(itap_path, estr, type, 1, 16); cfg.GetValue(itap_path, estr, type, 1, 16);
if (type.compare("itap")) { if (type.compare("itap"))
{
fprintf(stderr, "%s = %s is not 'itap' type!\n", itap_path.c_str(), type.c_str()); fprintf(stderr, "%s = %s is not 'itap' type!\n", itap_path.c_str(), type.c_str());
return true; return true;
} }
} else { }
else
{
fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module); fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module);
return true; return true;
} }
@ -580,22 +656,30 @@ bool CQnetITAP::ReadConfig(const char *cfgFile)
cfg.GetValue(itap_path+"_ap_mode", type, AP_MODE); cfg.GetValue(itap_path+"_ap_mode", type, AP_MODE);
itap_path.append("_callsign"); itap_path.append("_callsign");
if (cfg.KeyExists(itap_path)) { if (cfg.KeyExists(itap_path))
{
if (cfg.GetValue(itap_path, type, RPTR, 3, 6)) if (cfg.GetValue(itap_path, type, RPTR, 3, 6))
return true; return true;
} else { }
else
{
itap_path.assign("ircddb_login"); itap_path.assign("ircddb_login");
if (cfg.KeyExists(itap_path)) { if (cfg.KeyExists(itap_path))
{
if (cfg.GetValue(itap_path, estr, RPTR, 3, 6)) if (cfg.GetValue(itap_path, estr, RPTR, 3, 6))
return true; return true;
} }
} }
int l = RPTR.length(); int l = RPTR.length();
if (l<3 || l>6) { if (l<3 || l>6)
{
printf("Call '%s' is invalid length!\n", RPTR.c_str()); printf("Call '%s' is invalid length!\n", RPTR.c_str());
return true; return true;
} else { }
for (int i=0; i<l; i++) { else
{
for (int i=0; i<l; i++)
{
if (islower(RPTR[i])) if (islower(RPTR[i]))
RPTR[i] = toupper(RPTR[i]); RPTR[i] = toupper(RPTR[i]);
} }
@ -612,7 +696,8 @@ void CQnetITAP::calcPFCS(const unsigned char *packet, unsigned char *pfcs)
{ {
unsigned short crc_dstar_ffff = 0xffff; unsigned short crc_dstar_ffff = 0xffff;
unsigned short tmp, short_c; unsigned short tmp, short_c;
unsigned short crc_tabccitt[256] = { unsigned short crc_tabccitt[256] =
{
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
@ -631,7 +716,8 @@ void CQnetITAP::calcPFCS(const unsigned char *packet, unsigned char *pfcs)
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78
}; };
for (int i = 0; i < 39 ; i++) { for (int i = 0; i < 39 ; i++)
{
short_c = 0x00ff & (unsigned short)packet[i]; short_c = 0x00ff & (unsigned short)packet[i];
tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; tmp = (crc_dstar_ffff & 0x00ff) ^ short_c;
crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp];
@ -648,12 +734,14 @@ void CQnetITAP::calcPFCS(const unsigned char *packet, unsigned char *pfcs)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
setbuf(stdout, NULL); setbuf(stdout, NULL);
if (2 != argc) { if (2 != argc)
{
fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]); fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]);
return 1; return 1;
} }
if ('-' == argv[1][0]) { if ('-' == argv[1][0])
{
printf("\nQnetITAP Version #%s Copyright (C) 2018-2019 by Thomas A. Early N7TAE\n", ITAP_VERSION); printf("\nQnetITAP Version #%s Copyright (C) 2018-2019 by Thomas A. Early N7TAE\n", ITAP_VERSION);
printf("QnetITAP comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("QnetITAP comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n");
@ -661,29 +749,31 @@ int main(int argc, const char **argv)
} }
const char *qn = strstr(argv[0], "qnitap"); const char *qn = strstr(argv[0], "qnitap");
if (NULL == qn) { if (NULL == qn)
{
fprintf(stderr, "Error finding 'qnitap' in %s!\n", argv[0]); fprintf(stderr, "Error finding 'qnitap' in %s!\n", argv[0]);
return 1; return 1;
} }
qn += 6; qn += 6;
int assigned_module; int assigned_module;
switch (*qn) { switch (*qn)
case NULL: {
assigned_module = -1; case NULL:
break; assigned_module = -1;
case 'a': break;
assigned_module = 0; case 'a':
break; assigned_module = 0;
case 'b': break;
assigned_module = 1; case 'b':
break; assigned_module = 1;
case 'c': break;
assigned_module = 2; case 'c':
break; assigned_module = 2;
default: break;
fprintf(stderr, "assigned module must be a, b or c\n"); default:
return 1; fprintf(stderr, "assigned module must be a, b or c\n");
return 1;
} }
CQnetITAP qnitap(assigned_module); CQnetITAP qnitap(assigned_module);

@ -31,7 +31,8 @@
#define CALL_SIZE 8 #define CALL_SIZE 8
#define IP_SIZE 15 #define IP_SIZE 15
enum REPLY_TYPE { enum REPLY_TYPE
{
RT_TIMEOUT, RT_TIMEOUT,
RT_ERROR, RT_ERROR,
RT_UNKNOWN, RT_UNKNOWN,
@ -44,22 +45,25 @@ enum REPLY_TYPE {
// Icom Terminal and Access Point Mode data structure // Icom Terminal and Access Point Mode data structure
#pragma pack(push, 1) #pragma pack(push, 1)
using SITAP = struct itap_tag { using SITAP = struct itap_tag
{
unsigned char length; unsigned char length;
// 41 for header // 41 for header
// 16 for voice // 16 for voice
unsigned char type; unsigned char type;
// 0x03U pong // 0x03U pong
// 0x10U header from icom // 0x10U header from icom
// 0x11U acknowledgment // 0x11U acknowledgment
// 0x12U data from icom (it's EOT if voice.sequence bit 0x40 is set) // 0x12U data from icom (it's EOT if voice.sequence bit 0x40 is set)
// 0x13U acknowledgment // 0x13U acknowledgment
// 0x20U header to icom // 0x20U header to icom
// 0x21U header acknowledgment // 0x21U header acknowledgment
// 0x22U data to icom // 0x22U data to icom
// 0x23U data acknowledgment // 0x23U data acknowledgment
union { union
struct { {
struct
{
unsigned char flag[3]; unsigned char flag[3];
unsigned char r2[8]; unsigned char r2[8];
unsigned char r1[8]; unsigned char r1[8];
@ -67,7 +71,8 @@ using SITAP = struct itap_tag {
unsigned char my[8]; unsigned char my[8];
unsigned char nm[4]; unsigned char nm[4];
} header; } header;
struct { struct
{
unsigned char counter; // ordinal counter is reset with each header unsigned char counter; // ordinal counter is reset with each header
unsigned char sequence; // is modulo 21 unsigned char sequence; // is modulo 21
unsigned char ambe[9]; unsigned char ambe[9];
@ -80,11 +85,13 @@ using SITAP = struct itap_tag {
class CFrame class CFrame
{ {
public: public:
CFrame(const unsigned char *buf) { CFrame(const unsigned char *buf)
{
memcpy(&frame.length, buf, buf[0]); memcpy(&frame.length, buf, buf[0]);
} }
CFrame(const CFrame &from) { CFrame(const CFrame &from)
{
memcpy(&frame.length, from.data(), from.size()); memcpy(&frame.length, from.data(), from.size());
} }

File diff suppressed because it is too large Load Diff

@ -43,24 +43,27 @@
#define TIMEOUT 50 #define TIMEOUT 50
#define LH_MAX_SIZE 39 #define LH_MAX_SIZE 39
using SREFDSVT = struct refdsvt_tag { using SREFDSVT = struct refdsvt_tag
{
unsigned char head[2]; unsigned char head[2];
SDSVT dsvt; SDSVT dsvt;
}; };
using STOREMOTE = struct to_remote_g2_tag { using STOREMOTE = struct to_remote_g2_tag
char cs[CALL_SIZE + 1]; {
CSockAddress addr; char cs[CALL_SIZE + 1];
char from_mod, to_mod; CSockAddress addr;
short countdown; char from_mod, to_mod;
short countdown;
bool auto_link, is_connected; bool auto_link, is_connected;
unsigned short in_streamid; // incoming from remote systems unsigned short in_streamid; // incoming from remote systems
unsigned short out_streamid; // outgoing to remote systems unsigned short out_streamid; // outgoing to remote systems
}; };
// This is the data payload in the map: inbound_list // This is the data payload in the map: inbound_list
// This is for inbound dongles // This is for inbound dongles
using SINBOUND = struct inbound_tag { using SINBOUND = struct inbound_tag
{
char call[CALL_SIZE + 1]; // the callsign of the remote char call[CALL_SIZE + 1]; // the callsign of the remote
CSockAddress addr; // IP and port of remote CSockAddress addr; // IP and port of remote
short countdown; // if countdown expires, the connection is terminated short countdown; // if countdown expires, the connection is terminated
@ -68,13 +71,15 @@ using SINBOUND = struct inbound_tag {
char client; // dvap, dvdongle char client; // dvap, dvdongle
}; };
using STRACING = struct tracing_tag { using STRACING = struct tracing_tag
{
unsigned short streamid; unsigned short streamid;
time_t last_time; // last time RF user talked time_t last_time; // last time RF user talked
}; };
class CQnetLink : CKRBase { class CQnetLink : CKRBase
{
public: public:
// functions // functions
CQnetLink(); CQnetLink();
@ -111,7 +116,7 @@ private:
bool only_admin_login, only_link_unlink, qso_details, log_debug, bool_rptr_ack, announce; bool only_admin_login, only_link_unlink, qso_details, log_debug, bool_rptr_ack, announce;
bool dplus_authorize, dplus_reflectors, dplus_repeaters, dplus_priority, uses_ipv6; bool dplus_authorize, dplus_reflectors, dplus_repeaters, dplus_priority, uses_ipv6;
unsigned short rmt_xrf_port, rmt_ref_port, rmt_dcs_port, my_g2_link_port, to_g2_external_port; unsigned short rmt_xrf_port, rmt_ref_port, rmt_dcs_port, my_g2_link_port, to_g2_external_port;
int delay_between, delay_before; int delay_between, delay_before;
std::string link_at_startup[3]; std::string link_at_startup[3];
unsigned int max_dongles, saved_max_dongles; unsigned int max_dongles, saved_max_dongles;
int rf_inactivity_timer[3]; int rf_inactivity_timer[3];
@ -129,7 +134,8 @@ private:
STOREMOTE to_remote_g2[3]; STOREMOTE to_remote_g2[3];
// broadcast for data arriving from xrf to local rptr // broadcast for data arriving from xrf to local rptr
struct brd_from_xrf_tag { struct brd_from_xrf_tag
{
unsigned short xrf_streamid; // streamid from xrf unsigned short xrf_streamid; // streamid from xrf
unsigned short rptr_streamid[2]; // generated streamid to rptr(s) unsigned short rptr_streamid[2]; // generated streamid to rptr(s)
} brd_from_xrf; } brd_from_xrf;
@ -137,7 +143,8 @@ private:
short brd_from_xrf_idx; short brd_from_xrf_idx;
// broadcast for data arriving from local rptr to xrf // broadcast for data arriving from local rptr to xrf
struct brd_from_rptr_tag { struct brd_from_rptr_tag
{
unsigned short from_rptr_streamid; unsigned short from_rptr_streamid;
unsigned short to_rptr_streamid[2]; unsigned short to_rptr_streamid[2];
} brd_from_rptr; } brd_from_rptr;
@ -172,7 +179,8 @@ private:
// END: TEXT crap // END: TEXT crap
// this is used for the "dashboard and qso_details" to avoid processing multiple headers // this is used for the "dashboard and qso_details" to avoid processing multiple headers
struct old_sid_tag { struct old_sid_tag
{
unsigned short sid; unsigned short sid;
} old_sid[3]; } old_sid[3];
@ -184,29 +192,35 @@ private:
const unsigned char endbytes[6] = { 0x55U, 0x55U, 0x55U, 0x55U, 0xC8U, 0x7AU }; const unsigned char endbytes[6] = { 0x55U, 0x55U, 0x55U, 0x55U, 0xC8U, 0x7AU };
time_t tnow; time_t tnow;
unsigned char dcs_seq[3] = { 0x00, 0x00, 0x00 }; unsigned char dcs_seq[3] = { 0x00, 0x00, 0x00 };
struct { struct
{
char mycall[9]; char mycall[9];
char sfx[5]; char sfx[5];
unsigned int dcs_rptr_seq; unsigned int dcs_rptr_seq;
} rptr_2_dcs[3] = { } rptr_2_dcs[3] =
{
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0} {" ", " ", 0}
}; };
struct { struct
{
char mycall[9]; char mycall[9];
char sfx[5]; char sfx[5];
unsigned int dcs_rptr_seq; unsigned int dcs_rptr_seq;
} ref_2_dcs[3] = { } ref_2_dcs[3] =
{
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0} {" ", " ", 0}
}; };
struct { struct
{
char mycall[9]; char mycall[9];
char sfx[5]; char sfx[5];
unsigned int dcs_rptr_seq; unsigned int dcs_rptr_seq;
} xrf_2_dcs[3] = { } xrf_2_dcs[3] =
{
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0}, {" ", " ", 0},
{" ", " ", 0} {" ", " ", 0}

@ -66,8 +66,8 @@ const unsigned char TYPE_ACK = 0x70U;
const unsigned char TYPE_NACK = 0x7FU; const unsigned char TYPE_NACK = 0x7FU;
CQnetModem::CQnetModem(int mod) CQnetModem::CQnetModem(int mod)
: assigned_module(mod) : assigned_module(mod)
, dstarSpace(0U) , dstarSpace(0U)
{ {
} }
@ -84,7 +84,8 @@ bool CQnetModem::GetBufferSize()
{ {
std::this_thread::sleep_for(std::chrono::seconds(2)); std::this_thread::sleep_for(std::chrono::seconds(2));
for (int i=0; i<6; i++) { for (int i=0; i<6; i++)
{
SMODEM frame; SMODEM frame;
frame.start = FRAME_START; frame.start = FRAME_START;
@ -94,10 +95,12 @@ bool CQnetModem::GetBufferSize()
if (3 != SendToModem(&frame.start)) if (3 != SendToModem(&frame.start))
return true; return true;
for (int count = 0; count < MAX_RESPONSES; count++) { for (int count = 0; count < MAX_RESPONSES; count++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
EModemResponse resp = GetModemData(&frame.start, sizeof(SVERSION)); EModemResponse resp = GetModemData(&frame.start, sizeof(SVERSION));
if (resp == EModemResponse::status) { if (resp == EModemResponse::status)
{
dstarSpace = frame.status.dsrsize; dstarSpace = frame.status.dsrsize;
printf("D-Star buffer will hold %u voice frames\n", dstarSpace); printf("D-Star buffer will hold %u voice frames\n", dstarSpace);
return false; return false;
@ -114,7 +117,8 @@ bool CQnetModem::GetVersion()
{ {
std::this_thread::sleep_for(std::chrono::seconds(2)); std::this_thread::sleep_for(std::chrono::seconds(2));
for (int i=0; i<6; i++) { for (int i=0; i<6; i++)
{
SVERSION frame; SVERSION frame;
frame.start = FRAME_START; frame.start = FRAME_START;
@ -124,10 +128,12 @@ bool CQnetModem::GetVersion()
if (3 != SendToModem(&frame.start)) if (3 != SendToModem(&frame.start))
return true; return true;
for (int count = 0; count < MAX_RESPONSES; count++) { for (int count = 0; count < MAX_RESPONSES; count++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
EModemResponse resp = GetModemData(&frame.start, sizeof(SVERSION)); EModemResponse resp = GetModemData(&frame.start, sizeof(SVERSION));
if (resp == EModemResponse::version && frame.length > 14U) { if (resp == EModemResponse::version && frame.length > 14U)
{
frame.version[frame.length-4U] = '\0'; // just to make sure! frame.version[frame.length-4U] = '\0'; // just to make sure!
if (0 == memcmp(frame.version, "MMDVM ", 6U)) if (0 == memcmp(frame.version, "MMDVM ", 6U))
hardwareType = EHardwareType::mmdvm; hardwareType = EHardwareType::mmdvm;
@ -145,7 +151,8 @@ bool CQnetModem::GetVersion()
hardwareType = EHardwareType::nano_dv; hardwareType = EHardwareType::nano_dv;
else if (0 == memcmp(frame.version, "MMDVM_HS-", 9U)) else if (0 == memcmp(frame.version, "MMDVM_HS-", 9U))
hardwareType = EHardwareType::mmdvm_hs; hardwareType = EHardwareType::mmdvm_hs;
else { else
{
hardwareType = EHardwareType::unknown; hardwareType = EHardwareType::unknown;
} }
@ -170,7 +177,8 @@ bool CQnetModem::SetFrequency()
if (hardwareType == EHardwareType::dvmega) if (hardwareType == EHardwareType::dvmega)
frame.length = 12U; frame.length = 12U;
else { else
{
frame.frequency.level = 255U; frame.frequency.level = 255U;
frame.frequency.ps = __builtin_bswap32(htonl(pocsagFrequency)); frame.frequency.ps = __builtin_bswap32(htonl(pocsagFrequency));
@ -188,22 +196,25 @@ bool CQnetModem::SetFrequency()
int count = 0; int count = 0;
bool got_ack = false; bool got_ack = false;
while (! got_ack) { while (! got_ack)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
switch (GetModemData(&frame.start, sizeof(SMODEM))) { switch (GetModemData(&frame.start, sizeof(SMODEM)))
case EModemResponse::ack: {
got_ack = true; case EModemResponse::ack:
break; got_ack = true;
case EModemResponse::nack: break;
fprintf(stderr, "SET_FREQ failed, returned NACK reason %u\n", frame.nack.reason); case EModemResponse::nack:
fprintf(stderr, "SET_FREQ failed, returned NACK reason %u\n", frame.nack.reason);
return true;
default:
if (++count >= MAX_RESPONSES)
{
fprintf(stderr, "The MMDVM is not responding to the SET_FREQ command!\n");
return true; return true;
default: }
if (++count >= MAX_RESPONSES) { break;
fprintf(stderr, "The MMDVM is not responding to the SET_FREQ command!\n");
return true;
}
break;
} }
} }
printf("Modem frequencies set: rx=%u(%u) tx=%u(%u) Hz\n", (uint32_t)(1.0E6 * RX_FREQUENCY), rx_frequency, (uint32_t)(1.0E6 * TX_FREQUENCY), tx_frequency); printf("Modem frequencies set: rx=%u(%u) tx=%u(%u) Hz\n", (uint32_t)(1.0E6 * RX_FREQUENCY), rx_frequency, (uint32_t)(1.0E6 * TX_FREQUENCY), tx_frequency);
@ -271,22 +282,25 @@ bool CQnetModem::SetConfiguration()
int count = 0; int count = 0;
bool got_ack = false; bool got_ack = false;
while (! got_ack) { while (! got_ack)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
switch (GetModemData(&frame.start, sizeof(SMODEM))) { switch (GetModemData(&frame.start, sizeof(SMODEM)))
case EModemResponse::ack: {
got_ack = true; case EModemResponse::ack:
break; got_ack = true;
case EModemResponse::nack: break;
fprintf(stderr, "SET_CONFIG failed, returned NACK reason %u\n", frame.nack.reason); case EModemResponse::nack:
fprintf(stderr, "SET_CONFIG failed, returned NACK reason %u\n", frame.nack.reason);
return true;
default:
if (++count >= MAX_RESPONSES)
{
fprintf(stderr, "The MMDVM is not responding to the SET_CONFIG command!\n");
return true; return true;
default: }
if (++count >= MAX_RESPONSES) { break;
fprintf(stderr, "The MMDVM is not responding to the SET_CONFIG command!\n");
return true;
}
break;
} }
} }
printf("Modem configuration set for D-Star only\n"); printf("Modem configuration set for D-Star only\n");
@ -296,19 +310,22 @@ bool CQnetModem::SetConfiguration()
int CQnetModem::OpenModem() int CQnetModem::OpenModem()
{ {
int fd = open(MODEM_DEVICE.c_str(), O_RDWR | O_NOCTTY | O_SYNC, 0); int fd = open(MODEM_DEVICE.c_str(), O_RDWR | O_NOCTTY | O_SYNC, 0);
if (fd < 0) { if (fd < 0)
{
printf("Failed to open device [%s], error=%d, message=%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno)); printf("Failed to open device [%s], error=%d, message=%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno));
return -1; return -1;
} }
if (isatty(fd) == 0) { if (isatty(fd) == 0)
{
printf("Device %s is not a tty device\n", MODEM_DEVICE.c_str()); printf("Device %s is not a tty device\n", MODEM_DEVICE.c_str());
close(fd); close(fd);
return -1; return -1;
} }
static termios t; static termios t;
if (tcgetattr(fd, &t) < 0) { if (tcgetattr(fd, &t) < 0)
{
printf("tcgetattr failed for %s, error=%d, message-%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno)); printf("tcgetattr failed for %s, error=%d, message-%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -325,7 +342,8 @@ int CQnetModem::OpenModem()
cfsetospeed(&t, B115200); cfsetospeed(&t, B115200);
cfsetispeed(&t, B115200); cfsetispeed(&t, B115200);
if (tcsetattr(fd, TCSANOW, &t) < 0) { if (tcsetattr(fd, TCSANOW, &t) < 0)
{
printf("tcsetattr failed for %s, error=%dm message=%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno)); printf("tcsetattr failed for %s, error=%dm message=%s\n", MODEM_DEVICE.c_str(), errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -336,28 +354,36 @@ int CQnetModem::OpenModem()
EModemResponse CQnetModem::GetModemData(unsigned char *buf, unsigned int size) EModemResponse CQnetModem::GetModemData(unsigned char *buf, unsigned int size)
{ {
if (size < 4U) { if (size < 4U)
{
fprintf(stderr, "Buffer size, %u is too small\n", size); fprintf(stderr, "Buffer size, %u is too small\n", size);
return EModemResponse::error; return EModemResponse::error;
} }
// Get the start byte // Get the start byte
int ret = read(serfd, buf, 1U); int ret = read(serfd, buf, 1U);
if (ret < 0) { if (ret < 0)
{
fprintf(stderr, "Error when reading frame start byte: %s\n", strerror(errno)); fprintf(stderr, "Error when reading frame start byte: %s\n", strerror(errno));
return EModemResponse::error; return EModemResponse::error;
} else if (ret == 0) { }
else if (ret == 0)
{
printf("READ START RETURNED A ZERO!\n"); printf("READ START RETURNED A ZERO!\n");
return EModemResponse::timeout; return EModemResponse::timeout;
} else if (buf[0] != FRAME_START) }
else if (buf[0] != FRAME_START)
return EModemResponse::timeout; return EModemResponse::timeout;
//get the length byte //get the length byte
ret = read(serfd, buf+1, 1U); ret = read(serfd, buf+1, 1U);
if (ret < 0) { if (ret < 0)
{
fprintf(stderr, "Error when reading frame length: %s\n", strerror(errno)); fprintf(stderr, "Error when reading frame length: %s\n", strerror(errno));
return EModemResponse::error; return EModemResponse::error;
} else if (ret == 0) { }
else if (ret == 0)
{
printf("READ LENGTH RETURNED A ZERO!\n"); printf("READ LENGTH RETURNED A ZERO!\n");
return(EModemResponse::timeout); return(EModemResponse::timeout);
} }
@ -366,62 +392,76 @@ EModemResponse CQnetModem::GetModemData(unsigned char *buf, unsigned int size)
// get the type byte // get the type byte
ret = read(serfd, buf+2, 1U); ret = read(serfd, buf+2, 1U);
if (ret < 0) { if (ret < 0)
{
fprintf(stderr, "Error when reading frame type: %s\n", strerror(errno)); fprintf(stderr, "Error when reading frame type: %s\n", strerror(errno));
return EModemResponse::error; return EModemResponse::error;
} else if (ret == 0) { }
else if (ret == 0)
{
printf("READ TYPE RETURNED A ZERO!\n"); printf("READ TYPE RETURNED A ZERO!\n");
return(EModemResponse::timeout); return(EModemResponse::timeout);
} }
// get the data // get the data
unsigned int length = buf[1]; unsigned int length = buf[1];
unsigned int offset = 3; unsigned int offset = 3;
while (offset < length) { while (offset < length)
{
ret = read(serfd, buf + offset, length - offset); ret = read(serfd, buf + offset, length - offset);
if (ret < 0) { if (ret < 0)
{
printf("Error when reading data: %s\n", strerror(errno)); printf("Error when reading data: %s\n", strerror(errno));
return EModemResponse::error; return EModemResponse::error;
} }
if (ret == 0) { if (ret == 0)
{
printf("READ DATA RETURNED A ZERO!\n"); printf("READ DATA RETURNED A ZERO!\n");
return(EModemResponse::timeout); return(EModemResponse::timeout);
} else }
else
offset += ret; offset += ret;
} }
while (junk_count) { while (junk_count)
{
unsigned char junk[8]; unsigned char junk[8];
ret = read(serfd, junk, (junk_count > 8U) ? 8U : junk_count); ret = read(serfd, junk, (junk_count > 8U) ? 8U : junk_count);
if (ret < 0) { if (ret < 0)
{
printf("Error when reading junk: %s\n", strerror(errno)); printf("Error when reading junk: %s\n", strerror(errno));
return EModemResponse::error; return EModemResponse::error;
} else if (ret == 0) { }
else if (ret == 0)
{
printf("READ junk RETURNED A ZERO!\n"); printf("READ junk RETURNED A ZERO!\n");
return(EModemResponse::timeout); return(EModemResponse::timeout);
} else { }
else
{
junk_count -= (unsigned int)ret; junk_count -= (unsigned int)ret;
} }
} }
switch (buf[2]) { switch (buf[2])
case TYPE_ACK: {
return EModemResponse::ack; case TYPE_ACK:
case TYPE_NACK: return EModemResponse::ack;
return EModemResponse::nack; case TYPE_NACK:
case TYPE_HEADER: return EModemResponse::nack;
return EModemResponse::header; case TYPE_HEADER:
case TYPE_DATA: return EModemResponse::header;
return EModemResponse::data; case TYPE_DATA:
case TYPE_LOST: return EModemResponse::data;
return EModemResponse::lost; case TYPE_LOST:
case TYPE_EOT: return EModemResponse::lost;
return EModemResponse::eot; case TYPE_EOT:
case TYPE_VERSION: return EModemResponse::eot;
return EModemResponse::version; case TYPE_VERSION:
case TYPE_STATUS: return EModemResponse::version;
return EModemResponse::status; case TYPE_STATUS:
default: return EModemResponse::status;
return EModemResponse::error; default:
return EModemResponse::error;
}; };
} }
@ -438,7 +478,8 @@ void CQnetModem::Run(const char *cfgfile)
CTimer statusTimer; CTimer statusTimer;
CTimer deadTimer; CTimer deadTimer;
while (keep_running) { while (keep_running)
{
SMODEM frame; SMODEM frame;
frame.start = FRAME_START; frame.start = FRAME_START;
@ -454,55 +495,64 @@ void CQnetModem::Run(const char *cfgfile)
// don't care about writefds and exceptfds: // don't care about writefds and exceptfds:
int ret = select(maxfs+1, &readfds, NULL, NULL, &tv); int ret = select(maxfs+1, &readfds, NULL, NULL, &tv);
if (ret < 0) { if (ret < 0)
{
printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno)); printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno));
break; break;
} }
// check for a dead or disconnected radio // check for a dead or disconnected radio
if (10.0 < deadTimer.time()) { if (10.0 < deadTimer.time())
{
printf("no activity from radio for 10 sec. Exiting...\n"); printf("no activity from radio for 10 sec. Exiting...\n");
keep_running = false; keep_running = false;
} }
if (keep_running && FD_ISSET(serfd, &readfds)) { if (keep_running && FD_ISSET(serfd, &readfds))
{
deadTimer.start(); deadTimer.start();
switch (GetModemData(&frame.start, sizeof(SMODEM))) { switch (GetModemData(&frame.start, sizeof(SMODEM)))
case EModemResponse::data: {
case EModemResponse::header: case EModemResponse::data:
case EModemResponse::eot: case EModemResponse::header:
case EModemResponse::lost: case EModemResponse::eot:
if (ProcessModem(frame)) case EModemResponse::lost:
keep_running = false; if (ProcessModem(frame))
break; keep_running = false;
case EModemResponse::status: break;
if (frame.status.flags & 0x02U) case EModemResponse::status:
fprintf(stderr, "Modem ADC levels have overflowed\n"); if (frame.status.flags & 0x02U)
if (frame.status.flags & 0x04U) fprintf(stderr, "Modem ADC levels have overflowed\n");
fprintf(stderr, "Modem RX buffer has overflowed\n"); if (frame.status.flags & 0x04U)
if (frame.status.flags & 0x08U) fprintf(stderr, "Modem RX buffer has overflowed\n");
fprintf(stderr, "Modem TX buffer has overflowed\n"); if (frame.status.flags & 0x08U)
if (frame.status.flags & 0x20U) fprintf(stderr, "Modem TX buffer has overflowed\n");
fprintf(stderr, "Modem DAC levels have overflowed\n"); if (frame.status.flags & 0x20U)
dstarSpace = frame.status.dsrsize; fprintf(stderr, "Modem DAC levels have overflowed\n");
break; dstarSpace = frame.status.dsrsize;
default: break;
break; default:
break;
} }
FD_CLR(serfd, &readfds); FD_CLR(serfd, &readfds);
} }
if (keep_running && FD_ISSET(ug2m, &readfds)) { if (keep_running && FD_ISSET(ug2m, &readfds))
{
SDSVT dsvt; SDSVT dsvt;
ssize_t len = ToGate.Read(dsvt.title, sizeof(SDSVT)); ssize_t len = ToGate.Read(dsvt.title, sizeof(SDSVT));
if (len <= 0) { if (len <= 0)
{
break; break;
} }
if (0 == memcmp(dsvt.title, "DSVT", 4) && dsvt.id==0x20U && (dsvt.config==0x10U || dsvt.config==0x20U) && (len==56 || len==27)) { if (0 == memcmp(dsvt.title, "DSVT", 4) && dsvt.id==0x20U && (dsvt.config==0x10U || dsvt.config==0x20U) && (len==56 || len==27))
{
ProcessGateway(dsvt); ProcessGateway(dsvt);
} else { }
else
{
fprintf(stderr, "Unexpected data, returned %d bytes from the gateway: %02x", int(len), *dsvt.title); fprintf(stderr, "Unexpected data, returned %d bytes from the gateway: %02x", int(len), *dsvt.title);
for (ssize_t i=1; i<len; i++) for (ssize_t i=1; i<len; i++)
fprintf(stderr, " %02x", *(dsvt.title + int(i))); fprintf(stderr, " %02x", *(dsvt.title + int(i)));
@ -512,7 +562,8 @@ void CQnetModem::Run(const char *cfgfile)
FD_CLR(ug2m, &readfds); FD_CLR(ug2m, &readfds);
} }
if (keep_running) { if (keep_running)
{
//if (g2_is_active && PacketWait.time() > packet_wait) { //if (g2_is_active && PacketWait.time() > packet_wait) {
// // g2 has timed out // // g2 has timed out
// frame.length = 3U; // frame.length = 3U;
@ -520,17 +571,20 @@ void CQnetModem::Run(const char *cfgfile)
// queue.push(CFrame(&frame.start)); // queue.push(CFrame(&frame.start));
// g2_is_active = false; // g2_is_active = false;
//} //}
if (! queue.empty()) { if (! queue.empty())
{
// send queued D-Star frames to modem // send queued D-Star frames to modem
CFrame cframe = queue.front(); CFrame cframe = queue.front();
const unsigned char type = cframe.type(); const unsigned char type = cframe.type();
if ((type==TYPE_HEADER && dstarSpace>3U) || ((type==TYPE_DATA || type==TYPE_EOT || type==TYPE_LOST) && dstarSpace>0U)) { if ((type==TYPE_HEADER && dstarSpace>3U) || ((type==TYPE_DATA || type==TYPE_EOT || type==TYPE_LOST) && dstarSpace>0U))
{
SendToModem(cframe.data()); SendToModem(cframe.data());
queue.pop(); queue.pop();
dstarSpace -= (type==TYPE_HEADER) ? 4U : 1U; dstarSpace -= (type==TYPE_HEADER) ? 4U : 1U;
} }
} }
if (statusTimer.time() > 0.25) { if (statusTimer.time() > 0.25)
{
// request a status update every 250 milliseconds // request a status update every 250 milliseconds
frame.length = 3U; frame.length = 3U;
frame.type = TYPE_STATUS; frame.type = TYPE_STATUS;
@ -550,10 +604,13 @@ int CQnetModem::SendToModem(const unsigned char *buf)
size_t sent = 0; size_t sent = 0;
ssize_t length = buf[1]; ssize_t length = buf[1];
while ((ssize_t)sent < length) { while ((ssize_t)sent < length)
{
n = write(serfd, buf + sent, length - sent); n = write(serfd, buf + sent, length - sent);
if (n < 0) { if (n < 0)
if (EAGAIN != errno) { {
if (EAGAIN != errno)
{
printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno)); printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1; return -1;
} }
@ -569,7 +626,8 @@ void CQnetModem::ProcessGateway(const SDSVT &dsvt)
static std::string superframe; static std::string superframe;
SMODEM frame; // destination SMODEM frame; // destination
frame.start = FRAME_START; frame.start = FRAME_START;
if (0x10U == dsvt.config) { // write a Header packet if (0x10U == dsvt.config) // write a Header packet
{
superframe.clear(); superframe.clear();
frame.length = 44U; frame.length = 44U;
frame.type = TYPE_HEADER; frame.type = TYPE_HEADER;
@ -584,29 +642,39 @@ void CQnetModem::ProcessGateway(const SDSVT &dsvt)
PacketWait.start(); PacketWait.start();
if (LOG_QSO) if (LOG_QSO)
printf("Queued to %s flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", MODEM_DEVICE.c_str(), frame.header.flag[0], frame.header.flag[1], frame.header.flag[2], frame.header.ur, frame.header.r2, frame.header.r1, frame.header.my, frame.header.nm); printf("Queued to %s flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", MODEM_DEVICE.c_str(), frame.header.flag[0], frame.header.flag[1], frame.header.flag[2], frame.header.ur, frame.header.r2, frame.header.r1, frame.header.my, frame.header.nm);
} else { // write a voice data packet }
else // write a voice data packet
{
//const unsigned char sdsync[3] = { 0x55U, 0x2DU, 0x16U }; //const unsigned char sdsync[3] = { 0x55U, 0x2DU, 0x16U };
if (dsvt.ctrl & 0x40U) { if (dsvt.ctrl & 0x40U)
{
if (LOG_DEBUG && superframe.size()) if (LOG_DEBUG && superframe.size())
printf("Final order: %s\n", superframe.c_str()); printf("Final order: %s\n", superframe.c_str());
frame.length = 3U; frame.length = 3U;
frame.type = TYPE_EOT; frame.type = TYPE_EOT;
if (LOG_QSO) if (LOG_QSO)
printf("Queued modem end of transmission\n"); printf("Queued modem end of transmission\n");
} else { }
else
{
frame.length = 15U; frame.length = 15U;
frame.type = TYPE_DATA; frame.type = TYPE_DATA;
memcpy(frame.voice.ambe, dsvt.vasd.voice, 12); memcpy(frame.voice.ambe, dsvt.vasd.voice, 12);
if (LOG_DEBUG) { if (LOG_DEBUG)
{
const unsigned int ctrl = dsvt.ctrl & 0x3FU; const unsigned int ctrl = dsvt.ctrl & 0x3FU;
if (VoicePacketIsSync(dsvt.vasd.text)) { if (VoicePacketIsSync(dsvt.vasd.text))
if (superframe.size() > 65) { {
if (superframe.size() > 65)
{
printf("Frame order: %s\n", superframe.c_str()); printf("Frame order: %s\n", superframe.c_str());
superframe.clear(); superframe.clear();
} }
const char *ch = "#abcdefghijklmnopqrstuvwxyz"; const char *ch = "#abcdefghijklmnopqrstuvwxyz";
superframe.append(1, (ctrl<27U) ? ch[ctrl] : '%'); superframe.append(1, (ctrl<27U) ? ch[ctrl] : '%');
} else { }
else
{
const char *ch = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const char *ch = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ";
superframe.append(1, (ctrl<27U) ? ch[ctrl] : '*'); superframe.append(1, (ctrl<27U) ? ch[ctrl] : '*');
} }
@ -638,7 +706,8 @@ bool CQnetModem::ProcessModem(const SMODEM &frame)
dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1U : (('C'==RPTR_MOD) ? 0x2U : 0x3U); dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1U : (('C'==RPTR_MOD) ? 0x2U : 0x3U);
dsvt.streamid = htons(stream_id); dsvt.streamid = htons(stream_id);
if (frame.type == TYPE_HEADER) { // header if (frame.type == TYPE_HEADER) // header
{
nextctrl = 21U; nextctrl = 21U;
in_stream = first_voice_packet = true; in_stream = first_voice_packet = true;
dsvt.config = 0x10U; dsvt.config = 0x10U;
@ -653,20 +722,26 @@ bool CQnetModem::ProcessModem(const SMODEM &frame)
memcpy(dsvt.hdr.mycall, frame.header.my, 8); memcpy(dsvt.hdr.mycall, frame.header.my, 8);
memcpy(dsvt.hdr.sfx, frame.header.nm, 4); memcpy(dsvt.hdr.sfx, frame.header.nm, 4);
memcpy(dsvt.hdr.pfcs, frame.header.pfcs, 2); memcpy(dsvt.hdr.pfcs, frame.header.pfcs, 2);
if (ToGate.Write(dsvt.title, 56)) { if (ToGate.Write(dsvt.title, 56))
{
printf("ERROR: ProcessModem: Could not write gateway header packet\n"); printf("ERROR: ProcessModem: Could not write gateway header packet\n");
return true; return true;
} }
if (LOG_QSO) if (LOG_QSO)
printf("Sent DSVT to gateway, streamid=%04x flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.flag[0], dsvt.hdr.flag[1], dsvt.hdr.flag[2], dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx); printf("Sent DSVT to gateway, streamid=%04x flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.flag[0], dsvt.hdr.flag[1], dsvt.hdr.flag[2], dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx);
} else if (in_stream && (frame.type==TYPE_DATA || frame.type==TYPE_EOT || frame.type==TYPE_LOST)) { // ambe }
else if (in_stream && (frame.type==TYPE_DATA || frame.type==TYPE_EOT || frame.type==TYPE_LOST)) // ambe
{
const unsigned char sync[12] = { 0x9EU,0x8DU,0x32U,0x88U,0x26U,0x1AU,0x3FU,0x61U,0xE8U,0x55U,0x2DU,0x16U }; const unsigned char sync[12] = { 0x9EU,0x8DU,0x32U,0x88U,0x26U,0x1AU,0x3FU,0x61U,0xE8U,0x55U,0x2DU,0x16U };
const unsigned char silence[12] = { 0x9EU,0x8DU,0x32U,0x88U,0x26U,0x1AU,0x3FU,0x61U,0xE8U,0x70U,0x4FU,0x93U }; const unsigned char silence[12] = { 0x9EU,0x8DU,0x32U,0x88U,0x26U,0x1AU,0x3FU,0x61U,0xE8U,0x70U,0x4FU,0x93U };
dsvt.config = 0x20U; dsvt.config = 0x20U;
if (frame.type == TYPE_DATA) { if (frame.type == TYPE_DATA)
{
if (first_voice_packet) { // make sure the first voice packet is a sync frame if (first_voice_packet) // make sure the first voice packet is a sync frame
if (! VoicePacketIsSync(frame.voice.text)) { // create a quite sync voice packet {
if (! VoicePacketIsSync(frame.voice.text)) // create a quite sync voice packet
{
if (LOG_DEBUG) if (LOG_DEBUG)
printf("Warning: Inserting missing frame sync after header\n"); printf("Warning: Inserting missing frame sync after header\n");
dsvt.ctrl = 0U; dsvt.ctrl = 0U;
@ -677,18 +752,21 @@ bool CQnetModem::ProcessModem(const SMODEM &frame)
first_voice_packet = false; first_voice_packet = false;
} }
if (VoicePacketIsSync(frame.voice.text)) { if (VoicePacketIsSync(frame.voice.text))
{
if (nextctrl < 21U) if (nextctrl < 21U)
fprintf(stderr, "Warning: The last superframe had %u frames, inserting missing frame(s)\n", nextctrl); fprintf(stderr, "Warning: The last superframe had %u frames, inserting missing frame(s)\n", nextctrl);
memcpy(dsvt.vasd.voice, silence, 12U); memcpy(dsvt.vasd.voice, silence, 12U);
while (nextctrl < 21U) { while (nextctrl < 21U)
{
dsvt.ctrl = nextctrl++; dsvt.ctrl = nextctrl++;
ToGate.Write(dsvt.title, 27); ToGate.Write(dsvt.title, 27);
} }
nextctrl = 0x0U; nextctrl = 0x0U;
} }
if (nextctrl > 20U) { if (nextctrl > 20U)
{
fprintf(stderr, "Warning: nextctrl=%u, inserting missing sync frame\n", nextctrl); fprintf(stderr, "Warning: nextctrl=%u, inserting missing sync frame\n", nextctrl);
dsvt.ctrl = 0U; dsvt.ctrl = 0U;
memcpy(dsvt.vasd.voice, sync, 12U); memcpy(dsvt.vasd.voice, sync, 12U);
@ -697,16 +775,22 @@ bool CQnetModem::ProcessModem(const SMODEM &frame)
} }
memcpy(dsvt.vasd.voice, frame.voice.ambe, 12); memcpy(dsvt.vasd.voice, frame.voice.ambe, 12);
} else { }
else
{
if (frame.type == TYPE_LOST) if (frame.type == TYPE_LOST)
printf("Got a TYPE_LOST packet.\n"); printf("Got a TYPE_LOST packet.\n");
if (0U == nextctrl) { if (0U == nextctrl)
{
memcpy(dsvt.vasd.voice, sync, 12); memcpy(dsvt.vasd.voice, sync, 12);
} else { }
else
{
memcpy(dsvt.vasd.voice, silence, 12); memcpy(dsvt.vasd.voice, silence, 12);
} }
nextctrl |= 0x40U; nextctrl |= 0x40U;
if (LOG_QSO) { if (LOG_QSO)
{
if (frame.type == TYPE_EOT) if (frame.type == TYPE_EOT)
printf("Sent DSVT end of streamid=%04x\n", ntohs(dsvt.streamid)); printf("Sent DSVT end of streamid=%04x\n", ntohs(dsvt.streamid));
else else
@ -715,13 +799,17 @@ bool CQnetModem::ProcessModem(const SMODEM &frame)
in_stream = false; in_stream = false;
} }
dsvt.ctrl = nextctrl++; dsvt.ctrl = nextctrl++;
if (ToGate.Write(dsvt.title, 27)) { if (ToGate.Write(dsvt.title, 27))
{
printf("ERROR: ProcessModem: Could not write gateway voice packet\n"); printf("ERROR: ProcessModem: Could not write gateway voice packet\n");
return true; return true;
} }
} else { }
if (in_stream) { else
{
if (in_stream)
{
fprintf(stderr, "Warning! Unexpected frame: %02x", frame.start); fprintf(stderr, "Warning! Unexpected frame: %02x", frame.start);
for (unsigned int i=1U; i<frame.length; i++) for (unsigned int i=1U; i<frame.length; i++)
fprintf(stderr, ":%02x", *(&frame.start + i)); fprintf(stderr, ":%02x", *(&frame.start + i));
@ -743,12 +831,15 @@ bool CQnetModem::ReadConfig(const char *cfgFile)
const std::string estr; // an empty string const std::string estr; // an empty string
std::string type; std::string type;
std::string modem_path("module_"); std::string modem_path("module_");
if (0 > assigned_module) { if (0 > assigned_module)
{
// we need to find the lone mmdvmmodem module // we need to find the lone mmdvmmodem module
for (int i=0; i<3; i++) { for (int i=0; i<3; i++)
{
std::string test(modem_path); std::string test(modem_path);
test.append(1, 'a'+i); test.append(1, 'a'+i);
if (cfg.KeyExists(test)) { if (cfg.KeyExists(test))
{
cfg.GetValue(test, estr, type, 1, 16); cfg.GetValue(test, estr, type, 1, 16);
if (type.compare("mmdvmmodem")) if (type.compare("mmdvmmodem"))
continue; // this ain't it! continue; // this ain't it!
@ -757,20 +848,27 @@ bool CQnetModem::ReadConfig(const char *cfgFile)
break; break;
} }
} }
if (0 > assigned_module) { if (0 > assigned_module)
{
fprintf(stderr, "Error: no 'mmdvmmodem' module found\n!"); fprintf(stderr, "Error: no 'mmdvmmodem' module found\n!");
return true; return true;
} }
} else { }
else
{
// make sure mmdvmmodem module is defined // make sure mmdvmmodem module is defined
modem_path.append(1, 'a' + assigned_module); modem_path.append(1, 'a' + assigned_module);
if (cfg.KeyExists(modem_path)) { if (cfg.KeyExists(modem_path))
{
cfg.GetValue(modem_path, estr, type, 1, 16); cfg.GetValue(modem_path, estr, type, 1, 16);
if (type.compare("mmdvmmodem")) { if (type.compare("mmdvmmodem"))
{
fprintf(stderr, "%s = %s is not 'mmdvmmodem' type!\n", modem_path.c_str(), type.c_str()); fprintf(stderr, "%s = %s is not 'mmdvmmodem' type!\n", modem_path.c_str(), type.c_str());
return true; return true;
} }
} else { }
else
{
fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module); fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module);
return true; return true;
} }
@ -798,22 +896,30 @@ bool CQnetModem::ReadConfig(const char *cfgFile)
packet_wait = 1.0E-3 * double(PACKET_WAIT); packet_wait = 1.0E-3 * double(PACKET_WAIT);
modem_path.append("_callsign"); modem_path.append("_callsign");
if (cfg.KeyExists(modem_path)) { if (cfg.KeyExists(modem_path))
{
if (cfg.GetValue(modem_path, type, RPTR, 3, 6)) if (cfg.GetValue(modem_path, type, RPTR, 3, 6))
return true; return true;
} else { }
else
{
modem_path.assign("ircddb_login"); modem_path.assign("ircddb_login");
if (cfg.KeyExists(modem_path)) { if (cfg.KeyExists(modem_path))
{
if (cfg.GetValue(modem_path, estr, RPTR, 3, 6)) if (cfg.GetValue(modem_path, estr, RPTR, 3, 6))
return true; return true;
} }
} }
int l = RPTR.length(); int l = RPTR.length();
if (l<3 || l>6) { if (l<3 || l>6)
{
printf("Call '%s' is invalid length!\n", RPTR.c_str()); printf("Call '%s' is invalid length!\n", RPTR.c_str());
return true; return true;
} else { }
for (int i=0; i<l; i++) { else
{
for (int i=0; i<l; i++)
{
if (islower(RPTR[i])) if (islower(RPTR[i]))
RPTR[i] = toupper(RPTR[i]); RPTR[i] = toupper(RPTR[i]);
} }
@ -829,12 +935,14 @@ bool CQnetModem::ReadConfig(const char *cfgFile)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
setbuf(stdout, NULL); setbuf(stdout, NULL);
if (2 != argc) { if (2 != argc)
{
fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]); fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]);
return 1; return 1;
} }
if ('-' == argv[1][0]) { if ('-' == argv[1][0])
{
printf("\nQnetModem Version %s Copyright (C) 2019 by Thomas A. Early N7TAE\n", MODEM_VERSION); printf("\nQnetModem Version %s Copyright (C) 2019 by Thomas A. Early N7TAE\n", MODEM_VERSION);
printf("QnetModem comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("QnetModem comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\n"); printf("This is free software, and you are welcome to distribute it\n");
@ -843,29 +951,31 @@ int main(int argc, const char **argv)
} }
const char *qn = strstr(argv[0], "qnmodem"); const char *qn = strstr(argv[0], "qnmodem");
if (NULL == qn) { if (NULL == qn)
{
fprintf(stderr, "Error finding 'qnmodem' in %s!\n", argv[0]); fprintf(stderr, "Error finding 'qnmodem' in %s!\n", argv[0]);
return 1; return 1;
} }
qn += 7; qn += 7;
int assigned_module; int assigned_module;
switch (*qn) { switch (*qn)
case NULL: {
assigned_module = -1; case NULL:
break; assigned_module = -1;
case 'a': break;
assigned_module = 0; case 'a':
break; assigned_module = 0;
case 'b': break;
assigned_module = 1; case 'b':
break; assigned_module = 1;
case 'c': break;
assigned_module = 2; case 'c':
break; assigned_module = 2;
default: break;
fprintf(stderr, "assigned module must be a, b or c\n"); default:
return 1; fprintf(stderr, "assigned module must be a, b or c\n");
return 1;
} }
CQnetModem qnmodem(assigned_module); CQnetModem qnmodem(assigned_module);

@ -32,7 +32,8 @@
#define CALL_SIZE 8 #define CALL_SIZE 8
#define IP_SIZE 15 #define IP_SIZE 15
enum class EModemResponse { enum class EModemResponse
{
ack, ack,
nack, nack,
timeout, timeout,
@ -45,7 +46,8 @@ enum class EModemResponse {
version version
}; };
enum class EHardwareType { enum class EHardwareType
{
mmdvm, mmdvm,
dvmega, dvmega,
zumspot, zumspot,
@ -59,7 +61,8 @@ enum class EHardwareType {
// Icom Terminal and Access Point Mode data structure // Icom Terminal and Access Point Mode data structure
#pragma pack(push, 1) #pragma pack(push, 1)
using SVERSION = struct version_tag { using SVERSION = struct version_tag
{
unsigned char start; unsigned char start;
unsigned char length; unsigned char length;
unsigned char type; unsigned char type;
@ -67,39 +70,43 @@ using SVERSION = struct version_tag {
unsigned char version[251]; unsigned char version[251];
}; };
using SMODEM = struct mmodem_tag { using SMODEM = struct mmodem_tag
{
unsigned char start; // always 0xEOU unsigned char start; // always 0xEOU
unsigned char length; // 3 - 255 unsigned char length; // 3 - 255
unsigned char type; unsigned char type;
// 0x70U acknowledge from modem, ACK // 0x70U acknowledge from modem, ACK
// 0x7FU error from modem, NACK // 0x7FU error from modem, NACK
// 0x00U version // 0x00U version
// 0x01U status // 0x01U status
// 0x02U configure // 0x02U configure
// 0x03U mode // 0x03U mode
// 0x04U frequency // 0x04U frequency
// 0x10U header // 0x10U header
// 0x11U data // 0x11U data
// 0x12U transmission lost // 0x12U transmission lost
// 0x13U transmission end // 0x13U transmission end
union { union
{
unsigned char ack; // the type being acknowledged unsigned char ack; // the type being acknowledged
unsigned char mode; // 0 idle, 1 dstar, 2 dmr, 3 ysf, 99 calibration unsigned char mode; // 0 idle, 1 dstar, 2 dmr, 3 ysf, 99 calibration
struct { struct
{
unsigned char ack; // the type being acknowledged unsigned char ack; // the type being acknowledged
unsigned char reason; // reason for the NAK unsigned char reason; // reason for the NAK
// 1 - invalid command // 1 - invalid command
// 2 - wrong mode // 2 - wrong mode
// 3 - command too long // 3 - command too long
// 4 - data incorrect // 4 - data incorrect
// 5 - Not enough buffer space // 5 - Not enough buffer space
} nack; } nack;
// don't want to inflate the struct size, so it's here for reference only // don't want to inflate the struct size, so it's here for reference only
//struct { //struct {
// unsigned char protocol_version; // unsigned char protocol_version;
// unsigned char version[250]; // unsigned char version[250];
//} version; //} version;
struct { struct
{
unsigned char modes; // 0x1U dstar | 0x2 dmr | 0x4 system fusion unsigned char modes; // 0x1U dstar | 0x2 dmr | 0x4 system fusion
unsigned char status; // 0 idle, 1 dstar, 2 dmr, 3 system fusion, 99 calibration unsigned char status; // 0 idle, 1 dstar, 2 dmr, 3 system fusion, 99 calibration
unsigned char flags; // 0x1 Tx on, 0x2 adc overflow unsigned char flags; // 0x1 Tx on, 0x2 adc overflow
@ -108,7 +115,8 @@ using SMODEM = struct mmodem_tag {
unsigned char dm2size; // dmr timeslot 2 buffersize unsigned char dm2size; // dmr timeslot 2 buffersize
unsigned char ysfsize; // ysf buffersize unsigned char ysfsize; // ysf buffersize
} status; } status;
struct { struct
{
unsigned char flags; // 0x1 rx 0x2 tx 0x4 ptt 0x8 ysf lodev 0x10 debug 0x80 not duplex unsigned char flags; // 0x1 rx 0x2 tx 0x4 ptt 0x8 ysf lodev 0x10 debug 0x80 not duplex
unsigned char mode; // 0x1 dstar 0x2 drm 0x4 ysf 0x8 p25 0x10 nxdx 0x20 pocsag unsigned char mode; // 0x1 dstar 0x2 drm 0x4 ysf 0x8 p25 0x10 nxdx 0x20 pocsag
unsigned char tx_delay; // tx delay in 10 millisecond increments unsigned char tx_delay; // tx delay in 10 millisecond increments
@ -128,14 +136,16 @@ using SMODEM = struct mmodem_tag {
unsigned char ysf_tx_hang; unsigned char ysf_tx_hang;
unsigned char pocsag_tx; unsigned char pocsag_tx;
} config; } config;
struct { struct
{
unsigned char zero; // should be zero; unsigned char zero; // should be zero;
uint32_t rx; // receive frequency uint32_t rx; // receive frequency
uint32_t tx; // transmitter frequency uint32_t tx; // transmitter frequency
unsigned char level; // rf level for pocsag? unsigned char level; // rf level for pocsag?
uint32_t ps; // pocsag frequency, default 433000000U uint32_t ps; // pocsag frequency, default 433000000U
} frequency; } frequency;
struct { struct
{
unsigned char flag[3]; unsigned char flag[3];
unsigned char r2[8]; unsigned char r2[8];
unsigned char r1[8]; unsigned char r1[8];
@ -144,7 +154,8 @@ using SMODEM = struct mmodem_tag {
unsigned char nm[4]; unsigned char nm[4];
unsigned char pfcs[2]; unsigned char pfcs[2];
} header; } header;
struct { struct
{
unsigned char ambe[9]; unsigned char ambe[9];
unsigned char text[3]; unsigned char text[3];
} voice; } voice;
@ -155,15 +166,18 @@ using SMODEM = struct mmodem_tag {
class CFrame class CFrame
{ {
public: public:
CFrame(const unsigned char *buf) { CFrame(const unsigned char *buf)
{
memcpy(&frame.start, buf, buf[1]); memcpy(&frame.start, buf, buf[1]);
} }
CFrame(const CFrame &from) { CFrame(const CFrame &from)
{
memcpy(&frame.start, from.data(), from.size()); memcpy(&frame.start, from.data(), from.size());
} }
CFrame &operator=(const CFrame &from) { CFrame &operator=(const CFrame &from)
{
memcpy(&frame.start, from.data(), from.size()); memcpy(&frame.start, from.data(), from.size());
return *this; return *this;
} }

@ -39,9 +39,9 @@
#define RELAY_VERSION "QnetRelay-526" #define RELAY_VERSION "QnetRelay-526"
CQnetRelay::CQnetRelay(int mod) : CQnetRelay::CQnetRelay(int mod) :
assigned_module(mod), assigned_module(mod),
seed(time(NULL)), seed(time(NULL)),
COUNTER(0) COUNTER(0)
{ {
} }
@ -59,13 +59,15 @@ bool CQnetRelay::Initialize(const char *cfgfile)
int CQnetRelay::OpenSocket(const std::string &address, unsigned short port) int CQnetRelay::OpenSocket(const std::string &address, unsigned short port)
{ {
if (! port) { if (! port)
{
printf("ERROR: OpenSocket: non-zero port must be specified.\n"); printf("ERROR: OpenSocket: non-zero port must be specified.\n");
return -1; return -1;
} }
int fd = ::socket(PF_INET, SOCK_DGRAM, 0); int fd = ::socket(PF_INET, SOCK_DGRAM, 0);
if (fd < 0) { if (fd < 0)
{
printf("Cannot create the UDP socket, err: %d, %s\n", errno, strerror(errno)); printf("Cannot create the UDP socket, err: %d, %s\n", errno, strerror(errno));
return -1; return -1;
} }
@ -76,9 +78,11 @@ int CQnetRelay::OpenSocket(const std::string &address, unsigned short port)
addr.sin_port = htons(port); addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (! address.empty()) { if (! address.empty())
{
addr.sin_addr.s_addr = ::inet_addr(address.c_str()); addr.sin_addr.s_addr = ::inet_addr(address.c_str());
if (addr.sin_addr.s_addr == INADDR_NONE) { if (addr.sin_addr.s_addr == INADDR_NONE)
{
printf("The local address is invalid - %s\n", address.c_str()); printf("The local address is invalid - %s\n", address.c_str());
close(fd); close(fd);
return -1; return -1;
@ -86,13 +90,15 @@ int CQnetRelay::OpenSocket(const std::string &address, unsigned short port)
} }
int reuse = 1; int reuse = 1;
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1)
{
printf("Cannot set the UDP socket %s:%u option, err: %d, %s\n", address.c_str(), port, errno, strerror(errno)); printf("Cannot set the UDP socket %s:%u option, err: %d, %s\n", address.c_str(), port, errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
} }
if (::bind(fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { if (::bind(fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1)
{
printf("Cannot bind the UDP socket %s:%u address, err: %d, %s\n", address.c_str(), port, errno, strerror(errno)); printf("Cannot bind the UDP socket %s:%u address, err: %d, %s\n", address.c_str(), port, errno, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -119,7 +125,8 @@ bool CQnetRelay::Run(const char *cfgfile)
keep_running = true; keep_running = true;
while (keep_running) { while (keep_running)
{
fd_set readfds; fd_set readfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(msock, &readfds); FD_SET(msock, &readfds);
@ -129,7 +136,8 @@ bool CQnetRelay::Run(const char *cfgfile)
// don't care about writefds and exceptfds: // don't care about writefds and exceptfds:
// and we'll wait as long as needed // and we'll wait as long as needed
int ret = ::select(maxfs+1, &readfds, NULL, NULL, NULL); int ret = ::select(maxfs+1, &readfds, NULL, NULL, NULL);
if (ret < 0) { if (ret < 0)
{
printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno)); printf("ERROR: Run: select returned err=%d, %s\n", errno, strerror(errno));
break; break;
} }
@ -143,10 +151,12 @@ bool CQnetRelay::Run(const char *cfgfile)
socklen_t size = sizeof(sockaddr); socklen_t size = sizeof(sockaddr);
ssize_t len; ssize_t len;
if (FD_ISSET(msock, &readfds)) { if (FD_ISSET(msock, &readfds))
{
len = ::recvfrom(msock, buf, 100, 0, (sockaddr *)&addr, &size); len = ::recvfrom(msock, buf, 100, 0, (sockaddr *)&addr, &size);
if (len < 0) { if (len < 0)
{
fprintf(stderr, "ERROR: Run: recvfrom(mmdvmhost) return error %d: %s\n", errno, strerror(errno)); fprintf(stderr, "ERROR: Run: recvfrom(mmdvmhost) return error %d: %s\n", errno, strerror(errno));
break; break;
} }
@ -156,29 +166,37 @@ bool CQnetRelay::Run(const char *cfgfile)
} }
if (FD_ISSET(fd, &readfds)) { if (FD_ISSET(fd, &readfds))
{
len = ToGate.Read(buf, 100); len = ToGate.Read(buf, 100);
if (len < 0) { if (len < 0)
{
fprintf(stderr, "ERROR: Run: ToGate.Read() returned error %d: %s\n", errno, strerror(errno)); fprintf(stderr, "ERROR: Run: ToGate.Read() returned error %d: %s\n", errno, strerror(errno));
break; break;
} }
} }
if (len == 0) { if (len == 0)
{
fprintf(stderr, "DEBUG: Run: read zero bytes from %u\n", ntohs(addr.sin_port)); fprintf(stderr, "DEBUG: Run: read zero bytes from %u\n", ntohs(addr.sin_port));
continue; continue;
} }
if (0 == memcmp(buf, "DSRP", 4)) { if (0 == memcmp(buf, "DSRP", 4))
{
//printf("read %d bytes from MMDVMHost\n", (int)len); //printf("read %d bytes from MMDVMHost\n", (int)len);
if (ProcessMMDVM(len, buf)) if (ProcessMMDVM(len, buf))
break; break;
} else if (0 == ::memcmp(buf, "DSVT", 4)) { }
else if (0 == ::memcmp(buf, "DSVT", 4))
{
//printf("read %d bytes from MMDVMHost\n", (int)len); //printf("read %d bytes from MMDVMHost\n", (int)len);
if (ProcessGateway(len, buf)) if (ProcessGateway(len, buf))
break; break;
} else { }
else
{
char title[5]; char title[5];
for (int i=0; i<4; i++) for (int i=0; i<4; i++)
title[i] = (buf[i]>=0x20u && buf[i]<0x7fu) ? buf[i] : '.'; title[i] = (buf[i]>=0x20u && buf[i]<0x7fu) ? buf[i] : '.';
@ -210,7 +228,8 @@ int CQnetRelay::SendTo(const int fd, const unsigned char *buf, const int size, c
bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw) bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw)
{ {
if (27==len || 56==len) { //here is dstar data if (27==len || 56==len) //here is dstar data
{
SDSVT dsvt; SDSVT dsvt;
::memcpy(dsvt.title, raw, len); // transfer raw data to SDSVT struct ::memcpy(dsvt.title, raw, len); // transfer raw data to SDSVT struct
@ -219,7 +238,8 @@ bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw)
::memcpy(dsrp.title, "DSRP", 4); ::memcpy(dsrp.title, "DSRP", 4);
dsrp.voice.id = dsvt.streamid; // voice or header is the same position dsrp.voice.id = dsvt.streamid; // voice or header is the same position
dsrp.voice.seq = dsvt.ctrl; // ditto dsrp.voice.seq = dsvt.ctrl; // ditto
if (27 == len) { // write an AMBE packet if (27 == len) // write an AMBE packet
{
dsrp.tag = 0x21U; dsrp.tag = 0x21U;
if (log_qso && (dsrp.voice.seq & 0x40)) if (log_qso && (dsrp.voice.seq & 0x40))
printf("Sent DSRP end of streamid=%04x\n", ntohs(dsrp.voice.id)); printf("Sent DSRP end of streamid=%04x\n", ntohs(dsrp.voice.id));
@ -228,13 +248,17 @@ bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw)
dsrp.voice.err = 0; // NOT SURE WHERE TO GET THIS FROM THE INPUT buf dsrp.voice.err = 0; // NOT SURE WHERE TO GET THIS FROM THE INPUT buf
memcpy(dsrp.voice.ambe, dsvt.vasd.voice, 12); memcpy(dsrp.voice.ambe, dsvt.vasd.voice, 12);
int ret = SendTo(msock, dsrp.title, 21, MMDVM_IP, MMDVM_IN_PORT); int ret = SendTo(msock, dsrp.title, 21, MMDVM_IP, MMDVM_IN_PORT);
if (ret != 21) { if (ret != 21)
{
printf("ERROR: ProcessGateway: Could not write AMBE mmdvmhost packet\n"); printf("ERROR: ProcessGateway: Could not write AMBE mmdvmhost packet\n");
return true; return true;
} }
} else { // write a Header packet }
else // write a Header packet
{
dsrp.tag = 0x20U; dsrp.tag = 0x20U;
if (dsrp.header.seq) { if (dsrp.header.seq)
{
// printf("DEBUG: ProcessGateway: unexpected pkt.header.seq %d, resetting to 0\n", pkt.header.seq); // printf("DEBUG: ProcessGateway: unexpected pkt.header.seq %d, resetting to 0\n", pkt.header.seq);
dsrp.header.seq = 0; dsrp.header.seq = 0;
} }
@ -247,7 +271,8 @@ bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw)
memcpy(dsrp.header.nm, dsvt.hdr.sfx, 4); memcpy(dsrp.header.nm, dsvt.hdr.sfx, 4);
memcpy(dsrp.header.pfcs, dsvt.hdr.pfcs, 2); memcpy(dsrp.header.pfcs, dsvt.hdr.pfcs, 2);
int ret = SendTo(msock, dsrp.title, 49, MMDVM_IP, MMDVM_IN_PORT); int ret = SendTo(msock, dsrp.title, 49, MMDVM_IP, MMDVM_IN_PORT);
if (ret != 49) { if (ret != 49)
{
printf("ERROR: ProcessGateway: Could not write Header mmdvmhost packet\n"); printf("ERROR: ProcessGateway: Could not write Header mmdvmhost packet\n");
return true; return true;
} }
@ -255,7 +280,8 @@ bool CQnetRelay::ProcessGateway(const int len, const unsigned char *raw)
printf("Sent DSRP to %u, streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", MMDVM_IN_PORT, ntohs(dsrp.header.id), dsrp.header.ur, dsrp.header.r2, dsrp.header.r1, dsrp.header.my, dsrp.header.nm); printf("Sent DSRP to %u, streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", MMDVM_IN_PORT, ntohs(dsrp.header.id), dsrp.header.ur, dsrp.header.r2, dsrp.header.r1, dsrp.header.my, dsrp.header.nm);
} }
} else }
else
printf("DEBUG: ProcessGateway: unusual packet size read len=%d\n", len); printf("DEBUG: ProcessGateway: unusual packet size read len=%d\n", len);
return false; return false;
} }
@ -267,13 +293,17 @@ bool CQnetRelay::ProcessMMDVM(const int len, const unsigned char *raw)
if (len < 65) if (len < 65)
::memcpy(dsrp.title, raw, len); // transfer raw data to SDSRP struct ::memcpy(dsrp.title, raw, len); // transfer raw data to SDSRP struct
if (49==len || 21==len) { if (49==len || 21==len)
{
// grab the stream id if this is a header // grab the stream id if this is a header
if (49 == len) { if (49 == len)
{
if (dsrp.header.id == id) if (dsrp.header.id == id)
return false; return false;
id = dsrp.header.id; id = dsrp.header.id;
} else { }
else
{
if (dsrp.voice.id != id) if (dsrp.voice.id != id)
return false; return false;
} }
@ -289,7 +319,8 @@ bool CQnetRelay::ProcessMMDVM(const int len, const unsigned char *raw)
dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1U : (('C'==RPTR_MOD) ? 0x2U : 0x3U); dsvt.flagb[2] = ('B'==RPTR_MOD) ? 0x1U : (('C'==RPTR_MOD) ? 0x2U : 0x3U);
dsvt.streamid = id; dsvt.streamid = id;
if (49 == len) { // header if (49 == len) // header
{
dsvt.ctrl = 0x80; dsvt.ctrl = 0x80;
//memcpy(dsvt.hdr.flag, dsrp.header.flag, 41); //memcpy(dsvt.hdr.flag, dsrp.header.flag, 41);
memcpy(dsvt.hdr.flag, dsrp.header.flag, 3); memcpy(dsvt.hdr.flag, dsrp.header.flag, 3);
@ -299,17 +330,21 @@ bool CQnetRelay::ProcessMMDVM(const int len, const unsigned char *raw)
memcpy(dsvt.hdr.mycall, dsrp.header.my, 8); memcpy(dsvt.hdr.mycall, dsrp.header.my, 8);
memcpy(dsvt.hdr.sfx, dsrp.header.nm, 4); memcpy(dsvt.hdr.sfx, dsrp.header.nm, 4);
memcpy(dsvt.hdr.pfcs, dsrp.header.pfcs, 2); memcpy(dsvt.hdr.pfcs, dsrp.header.pfcs, 2);
if (ToGate.Write(dsvt.title, 56)) { if (ToGate.Write(dsvt.title, 56))
{
printf("ERROR: ProcessMMDVM: Could not write gateway header packet\n"); printf("ERROR: ProcessMMDVM: Could not write gateway header packet\n");
return true; return true;
} }
if (log_qso) if (log_qso)
printf("Sent DSVT streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx); printf("Sent DSVT streamid=%04x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2, dsvt.hdr.mycall, dsvt.hdr.sfx);
} else if (21 == len) { // ambe }
else if (21 == len) // ambe
{
dsvt.ctrl = dsrp.header.seq; dsvt.ctrl = dsrp.header.seq;
memcpy(dsvt.vasd.voice, dsrp.voice.ambe, 12); memcpy(dsvt.vasd.voice, dsrp.voice.ambe, 12);
if (ToGate.Write(dsvt.title, 27)) { if (ToGate.Write(dsvt.title, 27))
{
printf("ERROR: ProcessMMDVM: Could not write gateway voice packet\n"); printf("ERROR: ProcessMMDVM: Could not write gateway voice packet\n");
return true; return true;
} }
@ -317,9 +352,12 @@ bool CQnetRelay::ProcessMMDVM(const int len, const unsigned char *raw)
if (log_qso && dsvt.ctrl&0x40) if (log_qso && dsvt.ctrl&0x40)
printf("Sent DSVT end of streamid=%04x\n", ntohs(dsvt.streamid)); printf("Sent DSVT end of streamid=%04x\n", ntohs(dsvt.streamid));
} }
} else if (len < 65 && dsrp.tag == 0xAU) { }
else if (len < 65 && dsrp.tag == 0xAU)
{
// printf("MMDVM Poll: '%s'\n", (char *)mpkt.poll_msg); // printf("MMDVM Poll: '%s'\n", (char *)mpkt.poll_msg);
} else }
else
printf("DEBUG: ProcessMMDVM: unusual packet len=%d\n", len); printf("DEBUG: ProcessMMDVM: unusual packet len=%d\n", len);
return false; return false;
} }
@ -336,12 +374,15 @@ bool CQnetRelay::ReadConfig(const char *cfgFile)
std::string mmdvm_path("module_"); std::string mmdvm_path("module_");
std::string type; std::string type;
if (0 > assigned_module) { if (0 > assigned_module)
{
// we need to find the lone mmdvmhost module // we need to find the lone mmdvmhost module
for (int i=0; i<3; i++) { for (int i=0; i<3; i++)
{
std::string test(mmdvm_path); std::string test(mmdvm_path);
test.append(1, 'a'+i); test.append(1, 'a'+i);
if (cfg.KeyExists(test)) { if (cfg.KeyExists(test))
{
cfg.GetValue(test, estr, type, 1, 16); cfg.GetValue(test, estr, type, 1, 16);
if (type.compare("mmdvmhost")) if (type.compare("mmdvmhost"))
continue; // this ain't it! continue; // this ain't it!
@ -350,20 +391,27 @@ bool CQnetRelay::ReadConfig(const char *cfgFile)
break; break;
} }
} }
if (0 > assigned_module) { if (0 > assigned_module)
{
fprintf(stderr, "Error: no 'mmdvmhost' module found\n!"); fprintf(stderr, "Error: no 'mmdvmhost' module found\n!");
return true; return true;
} }
} else { }
else
{
// make sure mmdvmhost module is defined // make sure mmdvmhost module is defined
mmdvm_path.append(1, 'a' + assigned_module); mmdvm_path.append(1, 'a' + assigned_module);
if (cfg.KeyExists(mmdvm_path)) { if (cfg.KeyExists(mmdvm_path))
{
cfg.GetValue(mmdvm_path, estr, type, 1, 16); cfg.GetValue(mmdvm_path, estr, type, 1, 16);
if (type.compare("mmdvmhost")) { if (type.compare("mmdvmhost"))
{
fprintf(stderr, "%s = %s is not 'mmdvmhost' type!\n", mmdvm_path.c_str(), type.c_str()); fprintf(stderr, "%s = %s is not 'mmdvmhost' type!\n", mmdvm_path.c_str(), type.c_str());
return true; return true;
} }
} else { }
else
{
fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module); fprintf(stderr, "Module '%c' is not defined.\n", 'a'+assigned_module);
return true; return true;
} }
@ -386,12 +434,14 @@ bool CQnetRelay::ReadConfig(const char *cfgFile)
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
setbuf(stdout, NULL); setbuf(stdout, NULL);
if (2 != argc) { if (2 != argc)
{
fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]); fprintf(stderr, "usage: %s path_to_config_file\n", argv[0]);
return 1; return 1;
} }
if ('-' == argv[1][0]) { if ('-' == argv[1][0])
{
printf("\nQnetRelay Version #%s Copyright (C) 2018-2019 by Thomas A. Early N7TAE\n", RELAY_VERSION); printf("\nQnetRelay Version #%s Copyright (C) 2018-2019 by Thomas A. Early N7TAE\n", RELAY_VERSION);
printf("QnetRelay comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n"); printf("QnetRelay comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n"); printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n");
@ -399,28 +449,30 @@ int main(int argc, const char **argv)
} }
const char *qn = strstr(argv[0], "qnrelay"); const char *qn = strstr(argv[0], "qnrelay");
if (NULL == qn) { if (NULL == qn)
{
fprintf(stderr, "Error finding 'qnrelay' in %s!\n", argv[0]); fprintf(stderr, "Error finding 'qnrelay' in %s!\n", argv[0]);
return 1; return 1;
} }
qn += 7; qn += 7;
int module; int module;
switch (*qn) { switch (*qn)
case NULL: {
module = -1; case NULL:
break; module = -1;
case 'a': break;
module = 0; case 'a':
break; module = 0;
case 'b': break;
module = 1; case 'b':
break; module = 1;
case 'c': break;
module = 2; case 'c':
break; module = 2;
default: break;
fprintf(stderr, "assigned module must be a, b or c\n"); default:
return 1; fprintf(stderr, "assigned module must be a, b or c\n");
return 1;
} }
CQnetRelay qnmmdvm(module); CQnetRelay qnmmdvm(module);

@ -54,7 +54,8 @@ static int PLAY_WAIT, PLAY_DELAY;
static unsigned char silence[9] = { 0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8 }; static unsigned char silence[9] = { 0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8 };
static unsigned short crc_tabccitt[256] = { static unsigned short crc_tabccitt[256] =
{
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
@ -82,15 +83,18 @@ static bool ReadCfgFile()
std::string path = "module_"; std::string path = "module_";
path.append(1, 'a'+module); path.append(1, 'a'+module);
if (! cfg.KeyExists(path)) { if (! cfg.KeyExists(path))
{
fprintf(stderr, "%s not defined!\n", path.c_str()); fprintf(stderr, "%s not defined!\n", path.c_str());
return true; return true;
} }
cfg.GetValue(path, estr, type, 1, 16); cfg.GetValue(path, estr, type, 1, 16);
cfg.GetValue(path+"_callsign", type, REPEATER, 0, 6); cfg.GetValue(path+"_callsign", type, REPEATER, 0, 6);
if (REPEATER.length() < 4) { if (REPEATER.length() < 4)
if (cfg.GetValue("ircddb_login", estr, REPEATER, 3, 6)) { {
if (cfg.GetValue("ircddb_login", estr, REPEATER, 3, 6))
{
fprintf(stderr, "no Callsign for the repeater was found!\n"); fprintf(stderr, "no Callsign for the repeater was found!\n");
return true; return true;
} }
@ -108,7 +112,8 @@ static void calcPFCS(unsigned char rawbytes[56])
unsigned short tmp, short_c; unsigned short tmp, short_c;
short int i; short int i;
for (i = 15; i < 54 ; i++) { for (i = 15; i < 54 ; i++)
{
short_c = 0x00ff & (unsigned short)rawbytes[i]; short_c = 0x00ff & (unsigned short)rawbytes[i];
tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; tmp = (crc_dstar_ffff & 0x00ff) ^ short_c;
crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp];
@ -130,7 +135,8 @@ static void ToUpper(std::string &str)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
if (argc != 4) { if (argc != 4)
{
fprintf(stderr, "Usage: %s <module> <mycall> <yourcall>\n", argv[0]); fprintf(stderr, "Usage: %s <module> <mycall> <yourcall>\n", argv[0]);
fprintf(stderr, "Example: %s c n7tae xrf757al\n", argv[0]); fprintf(stderr, "Example: %s c n7tae xrf757al\n", argv[0]);
fprintf(stderr, "Where...\n"); fprintf(stderr, "Where...\n");
@ -140,25 +146,26 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
switch (argv[1][0]) { switch (argv[1][0])
case '0': {
case 'a': case '0':
case 'A': case 'a':
module = 0; case 'A':
break; module = 0;
case '1': break;
case 'b': case '1':
case 'B': case 'b':
module = 1; case 'B':
break; module = 1;
case '2': break;
case 'c': case '2':
case 'C': case 'c':
module = 2; case 'C':
break; module = 2;
default: break;
fprintf(stderr, "module must be 0, a, A, 1, b, B, 2, c or C, not %s\n", argv[1]); default:
return 1; fprintf(stderr, "module must be 0, a, A, 1, b, B, 2, c or C, not %s\n", argv[1]);
return 1;
} }
std::string cfgfile(CFG_DIR); std::string cfgfile(CFG_DIR);
@ -169,13 +176,15 @@ int main(int argc, char *argv[])
if (ReadCfgFile()) if (ReadCfgFile())
return 1; return 1;
if (REPEATER.size() > 6) { if (REPEATER.size() > 6)
{
printf("repeaterCallsign can not be more than 6 characters, %s is invalid\n", REPEATER.c_str()); printf("repeaterCallsign can not be more than 6 characters, %s is invalid\n", REPEATER.c_str());
return 1; return 1;
} }
ToUpper(REPEATER); ToUpper(REPEATER);
if (strlen(argv[2]) > 8) { if (strlen(argv[2]) > 8)
{
printf("MYCALL can not be more than 8 characters, %s is invalid\n", argv[2]); printf("MYCALL can not be more than 8 characters, %s is invalid\n", argv[2]);
return 1; return 1;
} }
@ -183,7 +192,8 @@ int main(int argc, char *argv[])
ToUpper(mycall); ToUpper(mycall);
if (strlen(argv[3]) > 8) { if (strlen(argv[3]) > 8)
{
printf("YOURCALL can not be more than 8 characters, %s is invalid\n", argv[3]); printf("YOURCALL can not be more than 8 characters, %s is invalid\n", argv[3]);
return 1; return 1;
} }
@ -191,7 +201,8 @@ int main(int argc, char *argv[])
ToUpper(yourcall); ToUpper(yourcall);
// replace underscores with spaces // replace underscores with spaces
auto pos = yourcall.find_first_of('_'); auto pos = yourcall.find_first_of('_');
while (yourcall.npos != pos) { while (yourcall.npos != pos)
{
yourcall[pos] = ' '; yourcall[pos] = ' ';
pos = yourcall.find_first_of('_'); pos = yourcall.find_first_of('_');
} }
@ -244,7 +255,8 @@ int main(int argc, char *argv[])
calcPFCS(pkt.title); calcPFCS(pkt.title);
// send the header // send the header
if (56 != ToGateway.Write(pkt.title, 56)) { if (56 != ToGateway.Write(pkt.title, 56))
{
printf("%s: ERROR: Couldn't send header!\n", argv[0]); printf("%s: ERROR: Couldn't send header!\n", argv[0]);
return 1; return 1;
} }
@ -253,65 +265,68 @@ int main(int argc, char *argv[])
pkt.config = 0x20U; pkt.config = 0x20U;
memcpy(pkt.vasd.voice, silence, 9); memcpy(pkt.vasd.voice, silence, 9);
for (int i=0; i<10; i++) { for (int i=0; i<10; i++)
{
/* start sending silence + text */ /* start sending silence + text */
pkt.ctrl = i; pkt.ctrl = i;
switch (i) { switch (i)
case 0: // sync voice frame {
pkt.vasd.text[0] = 0x55; case 0: // sync voice frame
pkt.vasd.text[1] = 0x2d; pkt.vasd.text[0] = 0x55;
pkt.vasd.text[2] = 0x16; pkt.vasd.text[1] = 0x2d;
break; pkt.vasd.text[2] = 0x16;
case 1: break;
pkt.vasd.text[0] = '@' ^ 0x70; case 1:
pkt.vasd.text[1] = RADIO_ID[0] ^ 0x4f; pkt.vasd.text[0] = '@' ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[1] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[0] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[1] ^ 0x93;
case 2: break;
pkt.vasd.text[0] = RADIO_ID[2] ^ 0x70; case 2:
pkt.vasd.text[1] = RADIO_ID[3] ^ 0x4f; pkt.vasd.text[0] = RADIO_ID[2] ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[4] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[3] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[4] ^ 0x93;
case 3: break;
pkt.vasd.text[0] = 'A' ^ 0x70; case 3:
pkt.vasd.text[1] = RADIO_ID[5] ^ 0x4f; pkt.vasd.text[0] = 'A' ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[6] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[5] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[6] ^ 0x93;
case 4: break;
pkt.vasd.text[0] = RADIO_ID[7] ^ 0x70; case 4:
pkt.vasd.text[1] = RADIO_ID[8] ^ 0x4f; pkt.vasd.text[0] = RADIO_ID[7] ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[9] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[8] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[9] ^ 0x93;
case 5: break;
pkt.vasd.text[0] = 'B' ^ 0x70; case 5:
pkt.vasd.text[1] = RADIO_ID[10] ^ 0x4f; pkt.vasd.text[0] = 'B' ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[11] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[10] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[11] ^ 0x93;
case 6: break;
pkt.vasd.text[0] = RADIO_ID[12] ^ 0x70; case 6:
pkt.vasd.text[1] = RADIO_ID[13] ^ 0x4f; pkt.vasd.text[0] = RADIO_ID[12] ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[14] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[13] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[14] ^ 0x93;
case 7: break;
pkt.vasd.text[0] = 'C' ^ 0x70; case 7:
pkt.vasd.text[1] = RADIO_ID[15] ^ 0x4f; pkt.vasd.text[0] = 'C' ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[16] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[15] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[16] ^ 0x93;
case 8: break;
pkt.vasd.text[0] = RADIO_ID[17] ^ 0x70; case 8:
pkt.vasd.text[1] = RADIO_ID[18] ^ 0x4f; pkt.vasd.text[0] = RADIO_ID[17] ^ 0x70;
pkt.vasd.text[2] = RADIO_ID[19] ^ 0x93; pkt.vasd.text[1] = RADIO_ID[18] ^ 0x4f;
break; pkt.vasd.text[2] = RADIO_ID[19] ^ 0x93;
case 9: // terminal voice packet break;
pkt.ctrl |= 0x40; case 9: // terminal voice packet
pkt.vasd.text[0] = 0x70; pkt.ctrl |= 0x40;
pkt.vasd.text[1] = 0x4f; pkt.vasd.text[0] = 0x70;
pkt.vasd.text[2] = 0x93; pkt.vasd.text[1] = 0x4f;
break; pkt.vasd.text[2] = 0x93;
break;
} }
if (27 != ToGateway.Write(pkt.title, 27)) { if (27 != ToGateway.Write(pkt.title, 27))
{
printf("%s: ERROR: could not send voice packet %d\n", argv[0], i); printf("%s: ERROR: could not send voice packet %d\n", argv[0], i);
return 1; return 1;
} }

@ -19,25 +19,31 @@
// for communicating with the g2 gateway on the internal port // for communicating with the g2 gateway on the internal port
#pragma pack(push, 1) // used internally by Icom stacks #pragma pack(push, 1) // used internally by Icom stacks
using SDSTR = struct dstr_tag { using SDSTR = struct dstr_tag
{
unsigned char pkt_id[4]; // 0 "DSTR" unsigned char pkt_id[4]; // 0 "DSTR"
unsigned short counter; // 4 unsigned short counter; // 4
unsigned char flag[3]; // 6 { 0x73, 0x12, 0x00 } unsigned char flag[3]; // 6 { 0x73, 0x12, 0x00 }
unsigned char remaining; // 9 the number of bytes left in the packet unsigned char remaining; // 9 the number of bytes left in the packet
union { union
struct { {
struct
{
unsigned char mycall[8]; // 10 unsigned char mycall[8]; // 10
unsigned char rpt[8]; // 18 unsigned char rpt[8]; // 18
} spkt; // total 26 } spkt; // total 26
struct { struct
{
unsigned char icm_id; // 10 unsigned char icm_id; // 10
unsigned char dst_rptr_id; // 11 unsigned char dst_rptr_id; // 11
unsigned char snd_rptr_id; // 12 unsigned char snd_rptr_id; // 12
unsigned char snd_term_id; // 13 unsigned char snd_term_id; // 13
unsigned short streamid; // 14 unsigned short streamid; // 14
unsigned char ctrl; // 16 sequence number hdr=0, voice%21, end|=0x40 unsigned char ctrl; // 16 sequence number hdr=0, voice%21, end|=0x40
union { union
struct { {
struct
{
unsigned char flag[3]; // 17 unsigned char flag[3]; // 17
unsigned char r2[8]; // 20 unsigned char r2[8]; // 20
unsigned char r1[8]; // 28 unsigned char r1[8]; // 28
@ -46,12 +52,15 @@ using SDSTR = struct dstr_tag {
unsigned char nm[4]; // 52 unsigned char nm[4]; // 52
unsigned char pfcs[2]; // 56 unsigned char pfcs[2]; // 56
} hdr; // total 58 } hdr; // total 58
union { union
struct { {
struct
{
unsigned char voice[9]; // 17 unsigned char voice[9]; // 17
unsigned char text[3]; // 26 unsigned char text[3]; // 26
} vasd; // total 29 } vasd; // total 29
struct { struct
{
unsigned char UNKNOWN[3]; // 17 not sure what this is, but g2_ doesn't seem to need it unsigned char UNKNOWN[3]; // 17 not sure what this is, but g2_ doesn't seem to need it
unsigned char voice[9]; // 20 unsigned char voice[9]; // 20
unsigned char text[3]; // 29 unsigned char text[3]; // 29
@ -65,7 +74,8 @@ using SDSTR = struct dstr_tag {
// for the g2 external port and between QnetGateway programs // for the g2 external port and between QnetGateway programs
#pragma pack(push, 1) #pragma pack(push, 1)
using SDSVT = struct dsvt_tag { using SDSVT = struct dsvt_tag
{
unsigned char title[4]; // 0 "DSVT" unsigned char title[4]; // 0 "DSVT"
unsigned char config; // 4 0x10 is hdr 0x20 is vasd unsigned char config; // 4 0x10 is hdr 0x20 is vasd
unsigned char flaga[3]; // 5 zeros unsigned char flaga[3]; // 5 zeros
@ -73,8 +83,10 @@ using SDSVT = struct dsvt_tag {
unsigned char flagb[3]; // 9 0x0 0x1 (A:0x3 B:0x1 C:0x2) unsigned char flagb[3]; // 9 0x0 0x1 (A:0x3 B:0x1 C:0x2)
unsigned short streamid;// 12 unsigned short streamid;// 12
unsigned char ctrl; // 14 hdr: 0x80 vsad: framecounter (mod 21) unsigned char ctrl; // 14 hdr: 0x80 vsad: framecounter (mod 21)
union { union
struct { // index {
struct // index
{
unsigned char flag[3]; // 15 unsigned char flag[3]; // 15
unsigned char rpt1[8]; // 18 unsigned char rpt1[8]; // 18
unsigned char rpt2[8]; // 26 unsigned char rpt2[8]; // 26
@ -83,11 +95,13 @@ using SDSVT = struct dsvt_tag {
unsigned char sfx[4]; // 50 unsigned char sfx[4]; // 50
unsigned char pfcs[2]; // 54 unsigned char pfcs[2]; // 54
} hdr; // total 56 } hdr; // total 56
struct { struct
{
unsigned char voice[9]; // 15 unsigned char voice[9]; // 15
unsigned char text[3]; // 24 unsigned char text[3]; // 24
} vasd; // voice and slow data total 27 } vasd; // voice and slow data total 27
struct { struct
{
unsigned char voice[9]; // 15 unsigned char voice[9]; // 15
unsigned char end[6]; // 24 unsigned char end[6]; // 24
} vend; // voice and end seq total 32 (for DPlus) } vend; // voice and end seq total 32 (for DPlus)
@ -97,19 +111,22 @@ using SDSVT = struct dsvt_tag {
// for mmdvm // for mmdvm
#pragma pack(push, 1) #pragma pack(push, 1)
using SDSRP = struct dsrp_tag { // offset size using SDSRP = struct dsrp_tag // offset size
{
unsigned char title[4]; // "DSRP" 0 unsigned char title[4]; // "DSRP" 0
unsigned char tag; // Poll : 0xA 4 unsigned char tag; // Poll : 0xA 4
// Header : busy ? 0x22 : 0x20 // Header : busy ? 0x22 : 0x20
// Voice : busy ? 0x23 : 0x21 // Voice : busy ? 0x23 : 0x21
union { union
{
unsigned char poll_msg[59]; // space for text 5 variable, max is 64, including trailing null unsigned char poll_msg[59]; // space for text 5 variable, max is 64, including trailing null
struct { struct
{
unsigned short id; // random id number 5 unsigned short id; // random id number 5
unsigned char seq; // 0x0 7 unsigned char seq; // 0x0 7
unsigned char flag[3]; // 0x80 Dstar Data 8 unsigned char flag[3]; // 0x80 Dstar Data 8
// 0x40 Dstar Repeater // 0x40 Dstar Repeater
// 0x01 Dstar Relay Unavailable // 0x01 Dstar Relay Unavailable
unsigned char r2[8]; // Repeater 2 11 unsigned char r2[8]; // Repeater 2 11
unsigned char r1[8]; // Repeater 1 19 unsigned char r1[8]; // Repeater 1 19
unsigned char ur[8]; // Your Call 27 unsigned char ur[8]; // Your Call 27
@ -117,10 +134,11 @@ using SDSRP = struct dsrp_tag { // offset size
unsigned char nm[4]; // Name 43 unsigned char nm[4]; // Name 43
unsigned char pfcs[2]; // checksum 47 49 unsigned char pfcs[2]; // checksum 47 49
} header; } header;
struct { struct
{
unsigned short id; // random id number 5 unsigned short id; // random id number 5
unsigned char seq; // sequence from 0 to 0x14 7 unsigned char seq; // sequence from 0 to 0x14 7
// if end then sequence |= 0x40 // if end then sequence |= 0x40
unsigned char err; // # of errors? 8 unsigned char err; // # of errors? 8
unsigned char ambe[12]; // voice + slow data 9 21 unsigned char ambe[12]; // voice + slow data 9 21
} voice; } voice;
@ -129,8 +147,9 @@ using SDSRP = struct dsrp_tag { // offset size
#pragma pack(pop) #pragma pack(pop)
#pragma pack(push, 1) #pragma pack(push, 1)
using SLINKFAMILY = struct link_family_tag { using SLINKFAMILY = struct link_family_tag
char title[4]; {
int family[3]; char title[4];
int family[3];
}; };
#pragma pack(pop) #pragma pack(pop)

@ -37,13 +37,16 @@ bool read_config(const char *cfgFile)
if (cfg.Initialize(cfgFile)) if (cfg.Initialize(cfgFile))
return true; return true;
for (int m=0; m<3; m++) { for (int m=0; m<3; m++)
{
std::string path("module_"); std::string path("module_");
path.append(std::to_string(m)); path.append(std::to_string(m));
std::string type; std::string type;
if (cfg.KeyExists(path)) { if (cfg.KeyExists(path))
{
cfg.GetValue(path, "", type, 1, 16); cfg.GetValue(path, "", type, 1, 16);
if (strcasecmp(type.c_str(), "dvap") && strcasecmp(type.c_str(), "dvrptr") && strcasecmp(type.c_str(), "mmdvm") && strcasecmp(type.c_str(), "itap")) { if (strcasecmp(type.c_str(), "dvap") && strcasecmp(type.c_str(), "dvrptr") && strcasecmp(type.c_str(), "mmdvm") && strcasecmp(type.c_str(), "itap"))
{
printf("module type '%s' is invalid\n", type.c_str()); printf("module type '%s' is invalid\n", type.c_str());
return true; return true;
} }
@ -69,7 +72,8 @@ int main(int argc, char *argv[])
{ {
char RADIO_ID[21]; char RADIO_ID[21];
if (argc != 4) { if (argc != 4)
{
printf("Usage: %s <module> <datFile> <txtMsg>\n", argv[0]); printf("Usage: %s <module> <datFile> <txtMsg>\n", argv[0]);
printf("Where...\n"); printf("Where...\n");
printf(" <module> is one of your modules: A, B or C\n"); printf(" <module> is one of your modules: A, B or C\n");
@ -87,7 +91,8 @@ int main(int argc, char *argv[])
if (islower(module)) if (islower(module))
module = toupper(module); module = toupper(module);
if ((module != 'A') && (module != 'B') && (module != 'C')) { if ((module != 'A') && (module != 'B') && (module != 'C'))
{
printf("module must be one of A B C\n"); printf("module must be one of A B C\n");
return 1; return 1;
} }
@ -96,7 +101,8 @@ int main(int argc, char *argv[])
snprintf(pathname, FILENAME_MAX, "%s/%s", announce_dir.c_str(), argv[2]); snprintf(pathname, FILENAME_MAX, "%s/%s", announce_dir.c_str(), argv[2]);
FILE *fp = fopen(pathname, "rb"); FILE *fp = fopen(pathname, "rb");
if (!fp) { if (!fp)
{
printf("Failed to find file %s for reading\n", pathname); printf("Failed to find file %s for reading\n", pathname);
return 1; return 1;
} }
@ -112,10 +118,13 @@ int main(int argc, char *argv[])
RADIO_ID[i] = '_'; RADIO_ID[i] = '_';
fp = fopen(qnvoice_file.c_str(), "w"); fp = fopen(qnvoice_file.c_str(), "w");
if (fp) { if (fp)
{
fprintf(fp, "%c_%s_%s\n", module, argv[2], RADIO_ID); fprintf(fp, "%c_%s_%s\n", module, argv[2], RADIO_ID);
fclose(fp); fclose(fp);
} else { }
else
{
printf("Failed to open %s for writing", qnvoice_file.c_str()); printf("Failed to open %s for writing", qnvoice_file.c_str());
return 1; return 1;
} }

@ -17,12 +17,13 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
using SECHO = struct echo_tag { using SECHO = struct echo_tag
{
bool is_linked; bool is_linked;
time_t last_time; time_t last_time;
unsigned short streamid; unsigned short streamid;
int fd; int fd;
char message[24]; char message[24];
SDSVT header; // only used in qnlink (qngateway writes the header to the file) SDSVT header; // only used in qnlink (qngateway writes the header to the file)
char file[FILENAME_MAX + 1]; char file[FILENAME_MAX + 1];
}; };

@ -44,33 +44,41 @@ public:
{ {
Clear(); Clear();
addr.ss_family = family; addr.ss_family = family;
if (AF_INET == family) { if (AF_INET == family)
{
auto addr4 = (struct sockaddr_in *)&addr; auto addr4 = (struct sockaddr_in *)&addr;
addr4->sin_port = htons(port); addr4->sin_port = htons(port);
if (address) { if (address)
{
if (0 == strncasecmp(address, "loc", 3)) if (0 == strncasecmp(address, "loc", 3))
inet_pton(AF_INET, "127.0.0.1", &(addr4->sin_addr)); inet_pton(AF_INET, "127.0.0.1", &(addr4->sin_addr));
else if (0 == strncasecmp(address, "any", 3)) else if (0 == strncasecmp(address, "any", 3))
inet_pton(AF_INET, "0.0.0.0", &(addr4->sin_addr)); inet_pton(AF_INET, "0.0.0.0", &(addr4->sin_addr));
else if (address) { else if (address)
{
if (1 > inet_pton(AF_INET, address, &(addr4->sin_addr))) if (1 > inet_pton(AF_INET, address, &(addr4->sin_addr)))
std::cerr << "Address Initialization Error: '" << address << "' is not a valdid IPV4 address!" << std::endl; std::cerr << "Address Initialization Error: '" << address << "' is not a valdid IPV4 address!" << std::endl;
} }
} }
} else if (AF_INET6 == family) { }
else if (AF_INET6 == family)
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
addr6->sin6_port = htons(port); addr6->sin6_port = htons(port);
if (address) { if (address)
{
if (0 == strncasecmp(address, "loc", 3)) if (0 == strncasecmp(address, "loc", 3))
inet_pton(AF_INET6, "::1", &(addr6->sin6_addr)); inet_pton(AF_INET6, "::1", &(addr6->sin6_addr));
else if (0 == strncasecmp(address, "any", 3)) else if (0 == strncasecmp(address, "any", 3))
inet_pton(AF_INET6, "::", &(addr6->sin6_addr)); inet_pton(AF_INET6, "::", &(addr6->sin6_addr));
else if (address) { else if (address)
{
if (1 > inet_pton(AF_INET6, address, &(addr6->sin6_addr))) if (1 > inet_pton(AF_INET6, address, &(addr6->sin6_addr)))
std::cerr << "Address Initialization Error: '" << address << "' is not a valid IPV6 address!" << std::endl; std::cerr << "Address Initialization Error: '" << address << "' is not a valid IPV6 address!" << std::endl;
} }
} }
} else }
else
std::cerr << "Error: Wrong address family type:" << family << " for [" << (address ? address : "NULL") << "]:" << port << std::endl; std::cerr << "Error: Wrong address family type:" << family << " for [" << (address ? address : "NULL") << "]:" << port << std::endl;
} }
@ -89,11 +97,14 @@ public:
{ {
if (addr.ss_family != rhs.addr.ss_family) if (addr.ss_family != rhs.addr.ss_family)
return false; return false;
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto l = (struct sockaddr_in *)&addr; auto l = (struct sockaddr_in *)&addr;
auto r = (struct sockaddr_in *)&rhs.addr; auto r = (struct sockaddr_in *)&rhs.addr;
return (l->sin_addr.s_addr == r->sin_addr.s_addr); return (l->sin_addr.s_addr == r->sin_addr.s_addr);
} else if (AF_INET6 == addr.ss_family) { }
else if (AF_INET6 == addr.ss_family)
{
auto l = (struct sockaddr_in6 *)&addr; auto l = (struct sockaddr_in6 *)&addr;
auto r = (struct sockaddr_in6 *)&rhs.addr; auto r = (struct sockaddr_in6 *)&rhs.addr;
return (0 == memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr))); return (0 == memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr)));
@ -105,11 +116,14 @@ public:
{ {
if (addr.ss_family != rhs.addr.ss_family) if (addr.ss_family != rhs.addr.ss_family)
return true; return true;
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto l = (struct sockaddr_in *)&addr; auto l = (struct sockaddr_in *)&addr;
auto r = (struct sockaddr_in *)&rhs.addr; auto r = (struct sockaddr_in *)&rhs.addr;
return (l->sin_addr.s_addr != r->sin_addr.s_addr); return (l->sin_addr.s_addr != r->sin_addr.s_addr);
} else if (AF_INET6 == addr.ss_family) { }
else if (AF_INET6 == addr.ss_family)
{
auto l = (struct sockaddr_in6 *)&addr; auto l = (struct sockaddr_in6 *)&addr;
auto r = (struct sockaddr_in6 *)&rhs.addr; auto r = (struct sockaddr_in6 *)&rhs.addr;
return (0 != memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr))); return (0 != memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr)));
@ -119,12 +133,16 @@ public:
bool AddressIsZero() const bool AddressIsZero() const
{ {
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
auto addr4 = (struct sockaddr_in *)&addr; {
auto addr4 = (struct sockaddr_in *)&addr;
return (addr4->sin_addr.s_addr == 0U); return (addr4->sin_addr.s_addr == 0U);
} else { }
else
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
for (unsigned int i=0; i<16; i++) { for (unsigned int i=0; i<16; i++)
{
if (addr6->sin6_addr.s6_addr[i]) if (addr6->sin6_addr.s6_addr[i])
return false; return false;
} }
@ -134,11 +152,14 @@ public:
void ClearAddress() void ClearAddress()
{ {
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto addr4 = (struct sockaddr_in *)&addr; auto addr4 = (struct sockaddr_in *)&addr;
addr4->sin_addr.s_addr = 0U; addr4->sin_addr.s_addr = 0U;
strcpy(straddr, "0.0.0.0"); strcpy(straddr, "0.0.0.0");
} else { }
else
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
memset(&(addr6->sin6_addr.s6_addr), 0, 16); memset(&(addr6->sin6_addr.s6_addr), 0, 16);
strcpy(straddr, "::"); strcpy(straddr, "::");
@ -149,41 +170,53 @@ public:
{ {
if (straddr[0]) if (straddr[0])
return straddr; return straddr;
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto addr4 = (struct sockaddr_in *)&addr; auto addr4 = (struct sockaddr_in *)&addr;
inet_ntop(AF_INET, &(addr4->sin_addr), straddr, INET6_ADDRSTRLEN); inet_ntop(AF_INET, &(addr4->sin_addr), straddr, INET6_ADDRSTRLEN);
} else if (AF_INET6 == addr.ss_family) { }
else if (AF_INET6 == addr.ss_family)
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
inet_ntop(AF_INET6, &(addr6->sin6_addr), straddr, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &(addr6->sin6_addr), straddr, INET6_ADDRSTRLEN);
} else { }
else
{
std::cerr << "Unknown socket family: " << addr.ss_family << std::endl; std::cerr << "Unknown socket family: " << addr.ss_family << std::endl;
} }
return straddr; return straddr;
} }
int GetFamily() const int GetFamily() const
{ {
return addr.ss_family; return addr.ss_family;
} }
unsigned short GetPort() const unsigned short GetPort() const
{ {
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto addr4 = (struct sockaddr_in *)&addr; auto addr4 = (struct sockaddr_in *)&addr;
return ntohs(addr4->sin_port); return ntohs(addr4->sin_port);
} else if (AF_INET6 == addr.ss_family) { }
else if (AF_INET6 == addr.ss_family)
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
return ntohs(addr6->sin6_port); return ntohs(addr6->sin6_port);
} else }
else
return 0; return 0;
} }
void SetPort(const uint16_t newport) void SetPort(const uint16_t newport)
{ {
if (AF_INET == addr.ss_family) { if (AF_INET == addr.ss_family)
{
auto addr4 = (struct sockaddr_in *)&addr; auto addr4 = (struct sockaddr_in *)&addr;
addr4->sin_port = htons(newport); addr4->sin_port = htons(newport);
} else if (AF_INET6 == addr.ss_family) { }
else if (AF_INET6 == addr.ss_family)
{
auto addr6 = (struct sockaddr_in6 *)&addr; auto addr6 = (struct sockaddr_in6 *)&addr;
addr6->sin6_port = htons(newport); addr6->sin6_port = htons(newport);
} }

@ -25,10 +25,10 @@
CTCPReaderWriterClient::CTCPReaderWriterClient(const std::string &address, int family, const std::string &port) : CTCPReaderWriterClient::CTCPReaderWriterClient(const std::string &address, int family, const std::string &port) :
m_address(address), m_address(address),
m_family(family), m_family(family),
m_port(port), m_port(port),
m_fd(-1) m_fd(-1)
{ {
} }
@ -51,17 +51,20 @@ bool CTCPReaderWriterClient::Open(const std::string &address, int family, const
bool CTCPReaderWriterClient::Open() bool CTCPReaderWriterClient::Open()
{ {
if (m_fd != -1) { if (m_fd != -1)
{
fprintf(stderr, "ERROR: port for '%s' is already open!\n", m_address.c_str()); fprintf(stderr, "ERROR: port for '%s' is already open!\n", m_address.c_str());
return true; return true;
} }
if (0 == m_address.size() || 0 == m_port.size() || 0 == std::stoul(m_port)) { if (0 == m_address.size() || 0 == m_port.size() || 0 == std::stoul(m_port))
{
fprintf(stderr, "ERROR: '[%s]:%s' is malformed!\n", m_address.c_str(), m_port.c_str()); fprintf(stderr, "ERROR: '[%s]:%s' is malformed!\n", m_address.c_str(), m_port.c_str());
return true; return true;
} }
if (AF_INET!=m_family && AF_INET6!=m_family && AF_UNSPEC!=m_family) { if (AF_INET!=m_family && AF_INET6!=m_family && AF_UNSPEC!=m_family)
{
fprintf(stderr, "ERROR: family must be AF_INET, AF_INET6 or AF_UNSPEC\n"); fprintf(stderr, "ERROR: family must be AF_INET, AF_INET6 or AF_UNSPEC\n");
return true; return true;
} }
@ -76,37 +79,47 @@ bool CTCPReaderWriterClient::Open()
struct addrinfo *res; struct addrinfo *res;
int s = EAI_AGAIN; int s = EAI_AGAIN;
int count = 0; int count = 0;
while (EAI_AGAIN==s and count++<20) { while (EAI_AGAIN==s and count++<20)
{
// connecting to a server, so we can wait until it's ready // connecting to a server, so we can wait until it's ready
s = getaddrinfo(m_address.c_str(), m_port.c_str(), &hints, &res); s = getaddrinfo(m_address.c_str(), m_port.c_str(), &hints, &res);
if (s && s != EAI_AGAIN) { if (s && s != EAI_AGAIN)
{
fprintf(stderr, "ERROR: getaddrinfo of %s: %s\n", m_address.c_str(), gai_strerror(s)); fprintf(stderr, "ERROR: getaddrinfo of %s: %s\n", m_address.c_str(), gai_strerror(s));
return true; return true;
} }
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
} }
if (EAI_AGAIN == s) { if (EAI_AGAIN == s)
fprintf(stderr, "ERROR getaddrinfo of %s failed 20 times\n", m_address.c_str()); {
return true; fprintf(stderr, "ERROR getaddrinfo of %s failed 20 times\n", m_address.c_str());
} return true;
}
struct addrinfo *rp; struct addrinfo *rp;
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next)
{
m_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); m_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (m_fd == -1) if (m_fd == -1)
continue; continue;
if (connect(m_fd, rp->ai_addr, rp->ai_addrlen)) { if (connect(m_fd, rp->ai_addr, rp->ai_addrlen))
{
Close(); Close();
continue; continue;
} else { }
else
{
char buf[INET6_ADDRSTRLEN]; char buf[INET6_ADDRSTRLEN];
void *addr; void *addr;
if (AF_INET == rp->ai_family) { if (AF_INET == rp->ai_family)
{
struct sockaddr_in *addr4 = (struct sockaddr_in *)rp->ai_addr; struct sockaddr_in *addr4 = (struct sockaddr_in *)rp->ai_addr;
addr = &(addr4->sin_addr); addr = &(addr4->sin_addr);
} else { }
else
{
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)rp->ai_addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)rp->ai_addr;
addr = &(addr6->sin6_addr); addr = &(addr6->sin6_addr);
} }
@ -117,7 +130,8 @@ bool CTCPReaderWriterClient::Open()
} }
freeaddrinfo(res); freeaddrinfo(res);
if (rp == NULL) { if (rp == NULL)
{
fprintf(stderr, "Could not connect to any system returned by %s\n", m_address.c_str()); fprintf(stderr, "Could not connect to any system returned by %s\n", m_address.c_str());
m_fd = -1; m_fd = -1;
return true; return true;
@ -130,13 +144,15 @@ int CTCPReaderWriterClient::ReadExact(unsigned char *buf, const unsigned int len
{ {
unsigned int offset = 0U; unsigned int offset = 0U;
do { do
{
int n = Read(buf + offset, length - offset); int n = Read(buf + offset, length - offset);
if (n < 0) if (n < 0)
return n; return n;
offset += n; offset += n;
} while ((length - offset) > 0U); }
while ((length - offset) > 0U);
return length; return length;
} }
@ -148,7 +164,8 @@ int CTCPReaderWriterClient::Read(unsigned char* buffer, const unsigned int lengt
assert(m_fd != -1); assert(m_fd != -1);
ssize_t len = recv(m_fd, buffer, length, 0); ssize_t len = recv(m_fd, buffer, length, 0);
if (len <= 0) { if (len <= 0)
{
if (len < 0) if (len < 0)
fprintf(stderr, "Error returned from recv, err=%d\n", errno); fprintf(stderr, "Error returned from recv, err=%d\n", errno);
return -1; return -1;
@ -167,11 +184,13 @@ int CTCPReaderWriterClient::ReadLine(std::string& line)
do do
{ {
resultCode = Read(&c, 1); resultCode = Read(&c, 1);
if(resultCode == 1) { if(resultCode == 1)
{
line += c; line += c;
len++; len++;
} }
} while(c != '\n' && resultCode == 1); }
while(c != '\n' && resultCode == 1);
return resultCode <= 0 ? resultCode : len; return resultCode <= 0 ? resultCode : len;
} }
@ -183,7 +202,8 @@ bool CTCPReaderWriterClient::Write(const unsigned char *buffer, const unsigned i
assert(m_fd != -1); assert(m_fd != -1);
ssize_t ret = send(m_fd, (char *)buffer, length, 0); ssize_t ret = send(m_fd, (char *)buffer, length, 0);
if (ret != ssize_t(length)) { if (ret != ssize_t(length))
{
if (ret < 0) if (ret < 0)
fprintf(stderr, "Error returned from send, err=%s\n", strerror(errno)); fprintf(stderr, "Error returned from send, err=%s\n", strerror(errno));
else else
@ -202,9 +222,10 @@ bool CTCPReaderWriterClient::WriteLine(const std::string& line)
size_t len = lineCopy.size(); size_t len = lineCopy.size();
bool result = true; bool result = true;
for(size_t i = 0; i < len && result; i++){ for(size_t i = 0; i < len && result; i++)
{
unsigned char c = lineCopy.at(i); unsigned char c = lineCopy.at(i);
result = Write(&c , 1); result = Write(&c, 1);
} }
return result; return result;
@ -212,7 +233,8 @@ bool CTCPReaderWriterClient::WriteLine(const std::string& line)
void CTCPReaderWriterClient::Close() void CTCPReaderWriterClient::Close()
{ {
if (m_fd != -1) { if (m_fd != -1)
{
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
} }

@ -33,7 +33,8 @@
#include <thread> #include <thread>
#include <chrono> #include <chrono>
class CTCPReaderWriterClient { class CTCPReaderWriterClient
{
public: public:
CTCPReaderWriterClient(const std::string &address, int family, const std::string &port); CTCPReaderWriterClient(const std::string &address, int family, const std::string &port);
CTCPReaderWriterClient(); CTCPReaderWriterClient();

@ -26,10 +26,12 @@ class CTimer
public: public:
CTimer() { start(); } CTimer() { start(); }
~CTimer() {} ~CTimer() {}
void start() { void start()
{
starttime = std::chrono::steady_clock::now(); starttime = std::chrono::steady_clock::now();
} }
double time() { double time()
{
std::chrono::duration<double> elapsed(std::chrono::steady_clock::now() - starttime); std::chrono::duration<double> elapsed(std::chrono::steady_clock::now() - starttime);
return elapsed.count(); return elapsed.count();
} }

@ -49,12 +49,14 @@ bool CUDPSocket::Open(const CSockAddress &addr)
{ {
// create socket // create socket
m_fd = socket(addr.GetFamily(), SOCK_DGRAM, 0); m_fd = socket(addr.GetFamily(), SOCK_DGRAM, 0);
if (0 > m_fd) { if (0 > m_fd)
{
std::cerr << "Cannot create socket on " << addr << ", " << strerror(errno) << std::endl; std::cerr << "Cannot create socket on " << addr << ", " << strerror(errno) << std::endl;
return true; return true;
} }
if (0 > fcntl(m_fd, F_SETFL, O_NONBLOCK)) { if (0 > fcntl(m_fd, F_SETFL, O_NONBLOCK))
{
std::cerr << "cannot set socket " << addr << " to non-blocking: " << strerror(errno) << std::endl; std::cerr << "cannot set socket " << addr << " to non-blocking: " << strerror(errno) << std::endl;
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
@ -62,7 +64,8 @@ bool CUDPSocket::Open(const CSockAddress &addr)
} }
const int reuse = 1; const int reuse = 1;
if (0 > setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))) { if (0 > setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)))
{
std::cerr << "Cannot set the UDP socket option on " << m_addr << ", err: " << strerror(errno) << std::endl; std::cerr << "Cannot set the UDP socket option on " << m_addr << ", err: " << strerror(errno) << std::endl;
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
@ -72,7 +75,8 @@ bool CUDPSocket::Open(const CSockAddress &addr)
// initialize sockaddr struct // initialize sockaddr struct
m_addr = addr; m_addr = addr;
if (0 != bind(m_fd, m_addr.GetCPointer(), m_addr.GetSize())) { if (0 != bind(m_fd, m_addr.GetCPointer(), m_addr.GetSize()))
{
std::cerr << "bind failed on " << m_addr << ", " << strerror(errno) << std::endl; std::cerr << "bind failed on " << m_addr << ", " << strerror(errno) << std::endl;
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
@ -84,7 +88,8 @@ bool CUDPSocket::Open(const CSockAddress &addr)
void CUDPSocket::Close(void) void CUDPSocket::Close(void)
{ {
if ( m_fd >= 0 ) { if ( m_fd >= 0 )
{
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
} }

@ -39,7 +39,8 @@ CUnixDgramReader::~CUnixDgramReader()
bool CUnixDgramReader::Open(const char *path) // returns true on failure bool CUnixDgramReader::Open(const char *path) // returns true on failure
{ {
fd = socket(AF_UNIX, SOCK_DGRAM, 0); fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (fd < 0) { if (fd < 0)
{
fprintf(stderr, "CUnixDgramReader::Open: socket() failed: %s\n", strerror(errno)); fprintf(stderr, "CUnixDgramReader::Open: socket() failed: %s\n", strerror(errno));
return true; return true;
} }
@ -51,7 +52,8 @@ bool CUnixDgramReader::Open(const char *path) // returns true on failure
strncpy(addr.sun_path+1, path, sizeof(addr.sun_path)-2); strncpy(addr.sun_path+1, path, sizeof(addr.sun_path)-2);
int rval = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); int rval = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (rval < 0) { if (rval < 0)
{
fprintf(stderr, "CUnixDgramReader::Open: bind() failed: %s\n", strerror(errno)); fprintf(stderr, "CUnixDgramReader::Open: bind() failed: %s\n", strerror(errno));
close(fd); close(fd);
fd = -1; fd = -1;
@ -98,13 +100,15 @@ ssize_t CUnixDgramWriter::Write(const void *buf, size_t size)
{ {
// open the socket // open the socket
int fd = socket(AF_UNIX, SOCK_DGRAM, 0); int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (fd < 0) { if (fd < 0)
{
fprintf(stderr, "Failed to open socket %s : %s\n", addr.sun_path+1, strerror(errno)); fprintf(stderr, "Failed to open socket %s : %s\n", addr.sun_path+1, strerror(errno));
return -1; return -1;
} }
// connect to the receiver // connect to the receiver
int rval = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); int rval = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (rval < 0) { if (rval < 0)
{
fprintf(stderr, "Failed to connect to socket %s : %s\n", addr.sun_path+1, strerror(errno)); fprintf(stderr, "Failed to connect to socket %s : %s\n", addr.sun_path+1, strerror(errno));
close(fd); close(fd);
return -1; return -1;
@ -112,7 +116,8 @@ ssize_t CUnixDgramWriter::Write(const void *buf, size_t size)
ssize_t written = 0; ssize_t written = 0;
int count = 0; int count = 0;
while (written <= 0) { while (written <= 0)
{
written = write(fd, buf, size); written = write(fd, buf, size);
if (written == (ssize_t)size) if (written == (ssize_t)size)
break; break;
@ -120,11 +125,13 @@ ssize_t CUnixDgramWriter::Write(const void *buf, size_t size)
fprintf(stderr, "ERROR: faied to write to %s : %s\n", addr.sun_path+1, strerror(errno)); fprintf(stderr, "ERROR: faied to write to %s : %s\n", addr.sun_path+1, strerror(errno));
else if (written == 0) else if (written == 0)
fprintf(stderr, "Warning: zero bytes written to %s\n", addr.sun_path+1); fprintf(stderr, "Warning: zero bytes written to %s\n", addr.sun_path+1);
else if (written != (ssize_t)size) { else if (written != (ssize_t)size)
{
fprintf(stderr, "ERROR: only %d of %d bytes written to %s\n", (int)written, (int)size, addr.sun_path+1); fprintf(stderr, "ERROR: only %d of %d bytes written to %s\n", (int)written, (int)size, addr.sun_path+1);
break; break;
} }
if (++count >= 100) { if (++count >= 100)
{
fprintf(stderr, "ERROR: Write failed after %d attempts\n", count-1); fprintf(stderr, "ERROR: Write failed after %d attempts\n", count-1);
break; break;
} }

@ -33,10 +33,14 @@ ssize_t CUnixPacket::Read(void *buffer, const ssize_t size)
if (0 > m_fd) if (0 > m_fd)
return -1; return -1;
ssize_t len = read(m_fd, buffer, size); ssize_t len = read(m_fd, buffer, size);
if (len < 1) { if (len < 1)
if (-1 == len) { {
if (-1 == len)
{
std::cerr << "Read error on '" << m_name << "': " << strerror(errno) << std::endl; std::cerr << "Read error on '" << m_name << "': " << strerror(errno) << std::endl;
} else if (0 == len) { }
else if (0 == len)
{
std::cerr << "Read error on '" << m_name << "': EOF" << std::endl; std::cerr << "Read error on '" << m_name << "': EOF" << std::endl;
} }
if (Restart()) if (Restart())
@ -52,10 +56,14 @@ bool CUnixPacket::Write(const void *buffer, const ssize_t size)
if (0 > m_fd) if (0 > m_fd)
return true; return true;
ssize_t written = write(m_fd, buffer, size); ssize_t written = write(m_fd, buffer, size);
if (written != size) { if (written != size)
if (-1 == written) { {
if (-1 == written)
{
std::cerr << "Write error on '" << m_name << "': " << strerror(errno) << std::endl; std::cerr << "Write error on '" << m_name << "': " << strerror(errno) << std::endl;
} else { }
else
{
std::cout << "Write error on '" << m_name << "': Only wrote " << written << " of " << size << " bytes" << std::endl; std::cout << "Write error on '" << m_name << "': Only wrote " << written << " of " << size << " bytes" << std::endl;
} }
return Restart(); return Restart();
@ -86,7 +94,8 @@ CUnixPacketServer::~CUnixPacketServer()
bool CUnixPacketServer::Open(const char *name, CKRBase *host) bool CUnixPacketServer::Open(const char *name, CKRBase *host)
{ {
m_server = socket(AF_UNIX, SOCK_SEQPACKET, 0); m_server = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (m_server < 0) { if (m_server < 0)
{
std::cerr << "Cannot open '" << name << "' socket: " << strerror(errno) << std::endl; std::cerr << "Cannot open '" << name << "' socket: " << strerror(errno) << std::endl;
return true; return true;
} }
@ -95,20 +104,23 @@ bool CUnixPacketServer::Open(const char *name, CKRBase *host)
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
memcpy(addr.sun_path+1, name, strlen(name)); memcpy(addr.sun_path+1, name, strlen(name));
if (-1 == bind(m_server, (struct sockaddr *)&addr, sizeof(addr))) { if (-1 == bind(m_server, (struct sockaddr *)&addr, sizeof(addr)))
{
std::cerr << "Cannot bind '" << name << "' socket: " << strerror(errno) << std::endl; std::cerr << "Cannot bind '" << name << "' socket: " << strerror(errno) << std::endl;
Close(); Close();
return true; return true;
} }
if (-1 == listen(m_server, 1)) { if (-1 == listen(m_server, 1))
{
std::cerr << "Cannot listen on '" << name << "' socket: " << strerror(errno) << std::endl; std::cerr << "Cannot listen on '" << name << "' socket: " << strerror(errno) << std::endl;
Close(); Close();
return true; return true;
} }
m_fd = accept(m_server, nullptr, 0); m_fd = accept(m_server, nullptr, 0);
if (m_fd < 0) { if (m_fd < 0)
{
std::cerr << "Cannot accept on '" << name << "' socket: " << strerror(errno) << std::endl; std::cerr << "Cannot accept on '" << name << "' socket: " << strerror(errno) << std::endl;
Close(); Close();
return true; return true;
@ -121,11 +133,13 @@ bool CUnixPacketServer::Open(const char *name, CKRBase *host)
void CUnixPacketServer::Close() void CUnixPacketServer::Close()
{ {
if (m_server >= 0) { if (m_server >= 0)
{
close(m_server); close(m_server);
m_server = -1; m_server = -1;
} }
if (m_fd >= 0) { if (m_fd >= 0)
{
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
} }
@ -139,7 +153,8 @@ CUnixPacketClient::~CUnixPacketClient()
bool CUnixPacketClient::Open(const char *name, CKRBase *host) bool CUnixPacketClient::Open(const char *name, CKRBase *host)
{ {
m_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); m_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (m_fd < 0) { if (m_fd < 0)
{
std::cerr << "Cannot open unix client socket " << name << std::endl; std::cerr << "Cannot open unix client socket " << name << std::endl;
return true; return true;
} }
@ -150,20 +165,26 @@ bool CUnixPacketClient::Open(const char *name, CKRBase *host)
memcpy(addr.sun_path+1, name, strlen(name)); memcpy(addr.sun_path+1, name, strlen(name));
int rval = -1; int rval = -1;
int tries = 0; int tries = 0;
while (rval < 0) { while (rval < 0)
{
rval = connect(m_fd, (struct sockaddr *)&addr, sizeof(addr)); rval = connect(m_fd, (struct sockaddr *)&addr, sizeof(addr));
if (rval < 0) { if (rval < 0)
if (ECONNREFUSED == errno) { {
if (ECONNREFUSED == errno)
{
if (0 == tries++ % 20) if (0 == tries++ % 20)
std::cout << "Waiting for " << name << " server to start..." << std::endl; std::cout << "Waiting for " << name << " server to start..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(250)); std::this_thread::sleep_for(std::chrono::milliseconds(250));
} else { }
else
{
std::cerr << "Cannot connect '" << name << "' socket: " << strerror(errno) << std::endl; std::cerr << "Cannot connect '" << name << "' socket: " << strerror(errno) << std::endl;
Close(); Close();
return true; return true;
} }
} }
if (! m_host->IsRunning()) { if (! m_host->IsRunning())
{
Close(); Close();
return true; return true;
} }
@ -176,7 +197,8 @@ bool CUnixPacketClient::Open(const char *name, CKRBase *host)
void CUnixPacketClient::Close() void CUnixPacketClient::Close()
{ {
if (m_fd >= 0) { if (m_fd >= 0)
{
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
} }

@ -22,7 +22,8 @@
#include "KRBase.h" #include "KRBase.h"
class CUnixPacket { class CUnixPacket
{
public: public:
CUnixPacket(); CUnixPacket();
virtual bool Open(const char *name, CKRBase *host) = 0; virtual bool Open(const char *name, CKRBase *host) = 0;
@ -37,7 +38,8 @@ protected:
char m_name[108]; char m_name[108];
}; };
class CUnixPacketServer : public CUnixPacket { class CUnixPacketServer : public CUnixPacket
{
public: public:
CUnixPacketServer(); CUnixPacketServer();
~CUnixPacketServer(); ~CUnixPacketServer();
@ -47,7 +49,8 @@ protected:
int m_server; int m_server;
}; };
class CUnixPacketClient : public CUnixPacket { class CUnixPacketClient : public CUnixPacket
{
public: public:
~CUnixPacketClient(); ~CUnixPacketClient();
bool Open(const char *name, CKRBase *host); bool Open(const char *name, CKRBase *host);

@ -3,39 +3,47 @@
#include <locale> #include <locale>
// trim from start (in place) // trim from start (in place)
static inline void ltrim(std::string &s) { static inline void ltrim(std::string &s)
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { {
return !std::isspace(ch); s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch)
})); {
return !std::isspace(ch);
}));
} }
// trim from end (in place) // trim from end (in place)
static inline void rtrim(std::string &s) { static inline void rtrim(std::string &s)
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { {
return !std::isspace(ch); s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch)
}).base(), s.end()); {
return !std::isspace(ch);
}).base(), s.end());
} }
// trim from both ends (in place) // trim from both ends (in place)
static inline void trim(std::string &s) { static inline void trim(std::string &s)
ltrim(s); {
rtrim(s); ltrim(s);
rtrim(s);
} }
// trim from start (copying) // trim from start (copying)
static inline std::string ltrim_copy(std::string s) { static inline std::string ltrim_copy(std::string s)
ltrim(s); {
return s; ltrim(s);
return s;
} }
// trim from end (copying) // trim from end (copying)
static inline std::string rtrim_copy(std::string s) { static inline std::string rtrim_copy(std::string s)
rtrim(s); {
return s; rtrim(s);
return s;
} }
// trim from both ends (copying) // trim from both ends (copying)
static inline std::string trim_copy(std::string s) { static inline std::string trim_copy(std::string s)
trim(s); {
return s; trim(s);
return s;
} }

@ -16,23 +16,24 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include "aprs.h" #include "aprs.h"
// This is called when header comes in from repeater // This is called when header comes in from repeater
void CAPRS::SelectBand(short int rptr_idx, unsigned short streamID) void CAPRS::SelectBand(short int rptr_idx, unsigned short streamID)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("ERROR in aprs_select_band, invalid mod %d\n", rptr_idx); printf("ERROR in aprs_select_band, invalid mod %d\n", rptr_idx);
return; return;
} }
@ -58,14 +59,17 @@ void CAPRS::ProcessText(unsigned short streamID, unsigned char seq, unsigned cha
short int rptr_idx = -1; short int rptr_idx = -1;
for (short int i = 0; i < 3; i++) { for (short int i = 0; i < 3; i++)
if (streamID == aprs_streamID[i].streamID) { {
if (streamID == aprs_streamID[i].streamID)
{
rptr_idx = i; rptr_idx = i;
break; break;
} }
} }
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("ERROR in aprs_process_text: rptr_idx %d is invalid\n", rptr_idx); printf("ERROR in aprs_process_text: rptr_idx %d is invalid\n", rptr_idx);
return; return;
} }
@ -73,7 +77,8 @@ void CAPRS::ProcessText(unsigned short streamID, unsigned char seq, unsigned cha
if ((seq & 0x40) == 0x40) if ((seq & 0x40) == 0x40)
return; return;
if ((seq & 0x1f) == 0x00) { if ((seq & 0x1f) == 0x00)
{
SyncIt(rptr_idx); SyncIt(rptr_idx);
return; return;
} }
@ -93,7 +98,8 @@ void CAPRS::ProcessText(unsigned short streamID, unsigned char seq, unsigned cha
return; return;
char *p = strchr((char*)aprs_data, ':'); char *p = strchr((char*)aprs_data, ':');
if (!p) { if (!p)
{
Reset(rptr_idx); Reset(rptr_idx);
return; return;
} }
@ -110,22 +116,25 @@ void CAPRS::ProcessText(unsigned short streamID, unsigned char seq, unsigned cha
sprintf(aprs_buf, "%s,qAR,%s:%s\r\n", hdr, m_rptr->mod[rptr_idx].call.c_str(), aud); sprintf(aprs_buf, "%s,qAR,%s:%s\r\n", hdr, m_rptr->mod[rptr_idx].call.c_str(), aud);
// printf("GPS-A=%s", aprs_buf); // printf("GPS-A=%s", aprs_buf);
int rc = aprs_sock.Write((unsigned char *)aprs_buf, strlen(aprs_buf)); int rc = aprs_sock.Write((unsigned char *)aprs_buf, strlen(aprs_buf));
if (rc == -1) { if (rc == -1)
{
if ((errno == EPIPE) || if ((errno == EPIPE) ||
(errno == ECONNRESET) || (errno == ECONNRESET) ||
(errno == ETIMEDOUT) || (errno == ETIMEDOUT) ||
(errno == ECONNABORTED) || (errno == ECONNABORTED) ||
(errno == ESHUTDOWN) || (errno == ESHUTDOWN) ||
(errno == EHOSTUNREACH) || (errno == EHOSTUNREACH) ||
(errno == ENETRESET) || (errno == ENETRESET) ||
(errno == ENETDOWN) || (errno == ENETDOWN) ||
(errno == ENETUNREACH) || (errno == ENETUNREACH) ||
(errno == EHOSTDOWN) || (errno == EHOSTDOWN) ||
(errno == ENOTCONN)) { (errno == ENOTCONN))
{
printf("CAPRS::ProcessText(): APRS_HOST closed connection, error=%d\n",errno); printf("CAPRS::ProcessText(): APRS_HOST closed connection, error=%d\n",errno);
aprs_sock.Close(); aprs_sock.Close();
} else /* if it is WOULDBLOCK, we will not go into a loop here */ }
else /* if it is WOULDBLOCK, we will not go into a loop here */
printf("CAPRS::ProcessText(): send error=%d\n", errno); printf("CAPRS::ProcessText(): send error=%d\n", errno);
} }
@ -137,7 +146,8 @@ void CAPRS::ProcessText(unsigned short streamID, unsigned char seq, unsigned cha
void CAPRS::Init() void CAPRS::Init()
{ {
/* Initialize the statistics on the APRS packets */ /* Initialize the statistics on the APRS packets */
for (short int rptr_idx = 0; rptr_idx < 3; rptr_idx++) { for (short int rptr_idx = 0; rptr_idx < 3; rptr_idx++)
{
aprs_pack[rptr_idx].al = al_none; aprs_pack[rptr_idx].al = al_none;
aprs_pack[rptr_idx].data[0] = '\0'; aprs_pack[rptr_idx].data[0] = '\0';
aprs_pack[rptr_idx].len = 0; aprs_pack[rptr_idx].len = 0;
@ -146,7 +156,8 @@ void CAPRS::Init()
aprs_pack[rptr_idx].is_sent = false; aprs_pack[rptr_idx].is_sent = false;
} }
for (short int i = 0; i < 3; i++) { for (short int i = 0; i < 3; i++)
{
aprs_streamID[i].streamID = 0; aprs_streamID[i].streamID = 0;
aprs_streamID[i].last_time = 0; aprs_streamID[i].last_time = 0;
} }
@ -157,7 +168,8 @@ void CAPRS::Init()
bool CAPRS::WriteData(short int rptr_idx, unsigned char *data) bool CAPRS::WriteData(short int rptr_idx, unsigned char *data)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::WriteData: rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::WriteData: rptr_idx %d is invalid\n", rptr_idx);
return false; return false;
} }
@ -165,7 +177,8 @@ bool CAPRS::WriteData(short int rptr_idx, unsigned char *data)
if (aprs_pack[rptr_idx].is_sent) if (aprs_pack[rptr_idx].is_sent)
return false; return false;
switch (aprs_pack[rptr_idx].sl) { switch (aprs_pack[rptr_idx].sl)
{
case sl_first: case sl_first:
aprs_pack[rptr_idx].buf[0] = data[0] ^ 0x70; aprs_pack[rptr_idx].buf[0] = data[0] ^ 0x70;
aprs_pack[rptr_idx].buf[1] = data[1] ^ 0x4f; aprs_pack[rptr_idx].buf[1] = data[1] ^ 0x4f;
@ -190,7 +203,8 @@ bool CAPRS::WriteData(short int rptr_idx, unsigned char *data)
void CAPRS::SyncIt(short int rptr_idx) void CAPRS::SyncIt(short int rptr_idx)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::SyncIt(): rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::SyncIt(): rptr_idx %d is invalid\n", rptr_idx);
return; return;
} }
@ -201,7 +215,8 @@ void CAPRS::SyncIt(short int rptr_idx)
void CAPRS::Reset(short int rptr_idx) void CAPRS::Reset(short int rptr_idx)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::Reset(): rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::Reset(): rptr_idx %d is invalid\n", rptr_idx);
return; return;
} }
@ -216,7 +231,8 @@ void CAPRS::Reset(short int rptr_idx)
unsigned int CAPRS::GetData(short int rptr_idx, unsigned char *data, unsigned int len) unsigned int CAPRS::GetData(short int rptr_idx, unsigned char *data, unsigned int len)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::GetData: rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::GetData: rptr_idx %d is invalid\n", rptr_idx);
return 0; return 0;
} }
@ -239,117 +255,157 @@ void CAPRS::Open(const std::string OWNER)
{ {
char snd_buf[512]; char snd_buf[512];
char rcv_buf[512]; char rcv_buf[512];
while (aprs_sock.Open(m_rptr->aprs.ip, AF_UNSPEC, std::to_string(m_rptr->aprs.port))) { while (aprs_sock.Open(m_rptr->aprs.ip, AF_UNSPEC, std::to_string(m_rptr->aprs.port)))
fprintf(stderr, "Failed to open %s, retry in 10 seconds...\n", m_rptr->aprs.ip.c_str()); {
std::this_thread::sleep_for(std::chrono::seconds(10)); fprintf(stderr, "Failed to open %s, retry in 10 seconds...\n", m_rptr->aprs.ip.c_str());
} std::this_thread::sleep_for(std::chrono::seconds(10));
}
/* login to aprs */ /* login to aprs */
//sprintf(snd_buf, "user %s pass %d vers QnetGateway 9 UDP 5 ", OWNER.c_str(), m_rptr->aprs_hash); //sprintf(snd_buf, "user %s pass %d vers QnetGateway 9 UDP 5 ", OWNER.c_str(), m_rptr->aprs_hash);
sprintf(snd_buf, "user %s pass %d vers QnetGateway-9 ", OWNER.c_str(), m_rptr->aprs_hash); sprintf(snd_buf, "user %s pass %d vers QnetGateway-9 ", OWNER.c_str(), m_rptr->aprs_hash);
/* add the user's filter */ /* add the user's filter */
if (m_rptr->aprs_filter.length()) { if (m_rptr->aprs_filter.length())
{
strcat(snd_buf, "filter "); strcat(snd_buf, "filter ");
strcat(snd_buf, m_rptr->aprs_filter.c_str()); strcat(snd_buf, m_rptr->aprs_filter.c_str());
} }
//printf("APRS Login command:[%s]\n", snd_buf); //printf("APRS Login command:[%s]\n", snd_buf);
strcat(snd_buf, "\r\n"); strcat(snd_buf, "\r\n");
while (true) { while (true)
int rc = aprs_sock.Write((unsigned char *)snd_buf, strlen(snd_buf)); {
if (rc < 0) { int rc = aprs_sock.Write((unsigned char *)snd_buf, strlen(snd_buf));
if (errno == EWOULDBLOCK) { if (rc < 0)
aprs_sock.Read((unsigned char *)rcv_buf, sizeof(rcv_buf)); {
if (errno == EWOULDBLOCK)
{
aprs_sock.Read((unsigned char *)rcv_buf, sizeof(rcv_buf));
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} else { }
else
{
printf("APRS Login command failed, error=%d\n", errno); printf("APRS Login command failed, error=%d\n", errno);
break; break;
} }
} else { }
else
{
// printf("APRS Login command sent\n"); // printf("APRS Login command sent\n");
break; break;
} }
} }
aprs_sock.Read((unsigned char *)rcv_buf, sizeof(rcv_buf)); aprs_sock.Read((unsigned char *)rcv_buf, sizeof(rcv_buf));
//printf("APRS Login returned: %s", rcv_buf); //printf("APRS Login returned: %s", rcv_buf);
return; return;
} }
bool CAPRS::AddData(short int rptr_idx, unsigned char *data) bool CAPRS::AddData(short int rptr_idx, unsigned char *data)
{ {
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::AddData(): rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::AddData(): rptr_idx %d is invalid\n", rptr_idx);
return false; return false;
} }
for (unsigned int i = 0; i < 5; i++) { for (unsigned int i = 0; i < 5; i++)
{
unsigned char c = data[i]; unsigned char c = data[i];
if ((aprs_pack[rptr_idx].al == al_none) && (c == '$')) { if ((aprs_pack[rptr_idx].al == al_none) && (c == '$'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_$1; aprs_pack[rptr_idx].al = al_$1;
} else if ((aprs_pack[rptr_idx].al == al_$1) && (c == '$')) { }
else if ((aprs_pack[rptr_idx].al == al_$1) && (c == '$'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_$2; aprs_pack[rptr_idx].al = al_$2;
} else if ((aprs_pack[rptr_idx].al == al_$2) && (c == 'C')) { }
else if ((aprs_pack[rptr_idx].al == al_$2) && (c == 'C'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_c1; aprs_pack[rptr_idx].al = al_c1;
} else if ((aprs_pack[rptr_idx].al == al_c1) && (c == 'R')) { }
else if ((aprs_pack[rptr_idx].al == al_c1) && (c == 'R'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_r1; aprs_pack[rptr_idx].al = al_r1;
} else if ((aprs_pack[rptr_idx].al == al_r1) && (c == 'C')) { }
else if ((aprs_pack[rptr_idx].al == al_r1) && (c == 'C'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_c2; aprs_pack[rptr_idx].al = al_c2;
} else if (aprs_pack[rptr_idx].al == al_c2) { }
else if (aprs_pack[rptr_idx].al == al_c2)
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_csum1; aprs_pack[rptr_idx].al = al_csum1;
} else if (aprs_pack[rptr_idx].al == al_csum1) { }
else if (aprs_pack[rptr_idx].al == al_csum1)
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_csum2; aprs_pack[rptr_idx].al = al_csum2;
} else if (aprs_pack[rptr_idx].al == al_csum2) { }
else if (aprs_pack[rptr_idx].al == al_csum2)
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_csum3; aprs_pack[rptr_idx].al = al_csum3;
} else if (aprs_pack[rptr_idx].al == al_csum3) { }
else if (aprs_pack[rptr_idx].al == al_csum3)
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_csum4; aprs_pack[rptr_idx].al = al_csum4;
} else if ((aprs_pack[rptr_idx].al == al_csum4) && (c == ',')) { }
else if ((aprs_pack[rptr_idx].al == al_csum4) && (c == ','))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
aprs_pack[rptr_idx].al = al_data; aprs_pack[rptr_idx].al = al_data;
} else if ((aprs_pack[rptr_idx].al == al_data) && (c != '\r')) { }
else if ((aprs_pack[rptr_idx].al == al_data) && (c != '\r'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
if (aprs_pack[rptr_idx].len >= 300) { if (aprs_pack[rptr_idx].len >= 300)
{
printf("ERROR in aprs_add_data: Expected END of APRS data\n"); printf("ERROR in aprs_add_data: Expected END of APRS data\n");
aprs_pack[rptr_idx].len = 0; aprs_pack[rptr_idx].len = 0;
aprs_pack[rptr_idx].al = al_none; aprs_pack[rptr_idx].al = al_none;
} }
} else if ((aprs_pack[rptr_idx].al == al_data) && (c == '\r')) { }
else if ((aprs_pack[rptr_idx].al == al_data) && (c == '\r'))
{
aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c;
aprs_pack[rptr_idx].len++; aprs_pack[rptr_idx].len++;
bool ok = CheckData(rptr_idx); bool ok = CheckData(rptr_idx);
if (ok) { if (ok)
{
aprs_pack[rptr_idx].al = al_end; aprs_pack[rptr_idx].al = al_end;
return true; return true;
} else { }
else
{
printf("BAD checksum in APRS data\n"); printf("BAD checksum in APRS data\n");
aprs_pack[rptr_idx].al = al_none; aprs_pack[rptr_idx].al = al_none;
aprs_pack[rptr_idx].len = 0; aprs_pack[rptr_idx].len = 0;
} }
} else { }
else
{
aprs_pack[rptr_idx].al = al_none; aprs_pack[rptr_idx].al = al_none;
aprs_pack[rptr_idx].len = 0; aprs_pack[rptr_idx].len = 0;
} }
@ -362,7 +418,8 @@ bool CAPRS::CheckData(short int rptr_idx)
unsigned int my_sum; unsigned int my_sum;
char buf[5]; char buf[5];
if ((rptr_idx < 0) || (rptr_idx > 2)) { if ((rptr_idx < 0) || (rptr_idx > 2))
{
printf("CAPRS::CheckData(): rptr_idx %d is invalid\n", rptr_idx); printf("CAPRS::CheckData(): rptr_idx %d is invalid\n", rptr_idx);
return false; return false;
} }
@ -383,10 +440,12 @@ unsigned int CAPRS::CalcCRC(unsigned char* buf, unsigned int len)
if (len <= 0) if (len <= 0)
return 0; return 0;
for (unsigned int j = 0; j < len; j++) { for (unsigned int j = 0; j < len; j++)
{
unsigned int c = buf[j]; unsigned int c = buf[j];
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++)
{
bool xor_val = (((my_crc ^ c) & 0x01) == 0x01); bool xor_val = (((my_crc ^ c) & 0x01) == 0x01);
my_crc >>= 1; my_crc >>= 1;
@ -406,10 +465,10 @@ CAPRS::CAPRS(SRPTR *prptr)
CAPRS::~CAPRS() CAPRS::~CAPRS()
{ {
aprs_sock.Close(); aprs_sock.Close();
} }
void CAPRS::CloseSock() void CAPRS::CloseSock()
{ {
aprs_sock.Close(); aprs_sock.Close();
} }

@ -30,12 +30,14 @@ enum aprs_level { al_none, al_$1, al_$2, al_c1, al_r1, al_c2, al_csum1, al_csum2
enum slow_level { sl_first, sl_second }; enum slow_level { sl_first, sl_second };
using SPORTIP = struct portip_tag { using SPORTIP = struct portip_tag
{
std::string ip; std::string ip;
int port; int port;
}; };
using SMOD = struct aprs_module { using SMOD = struct aprs_module
{
std::string call; /* KJ4NHF-B */ std::string call; /* KJ4NHF-B */
bool defined; bool defined;
std::string band; /* 23cm ... */ std::string band; /* 23cm ... */
@ -43,7 +45,8 @@ using SMOD = struct aprs_module {
std::string desc1, desc2, url, package_version; std::string desc1, desc2, url, package_version;
}; };
using SRPTR = struct aprs_info { using SRPTR = struct aprs_info
{
SPORTIP aprs; SPORTIP aprs;
std::string aprs_filter; std::string aprs_filter;
int aprs_hash; int aprs_hash;
@ -53,7 +56,8 @@ using SRPTR = struct aprs_info {
SMOD mod[3]; SMOD mod[3];
}; };
class CAPRS { class CAPRS
{
public: public:
// functions // functions
CAPRS(SRPTR *prptr); CAPRS(SRPTR *prptr);
@ -68,7 +72,8 @@ public:
private: private:
// data // data
struct { struct
{
aprs_level al; aprs_level al;
unsigned char data[300]; unsigned char data[300];
unsigned int len; unsigned int len;
@ -77,7 +82,8 @@ private:
bool is_sent; bool is_sent;
} aprs_pack[3]; } aprs_pack[3];
// lock down a stream per band // lock down a stream per band
struct { struct
{
unsigned short streamID; unsigned short streamID;
time_t last_time; time_t last_time;
} aprs_streamID[3]; } aprs_streamID[3];

@ -41,7 +41,7 @@ bool IRCClient::startWork()
void IRCClient::stopWork() void IRCClient::stopWork()
{ {
terminateThread = true; terminateThread = true;
client_thread.get(); client_thread.get();
} }
#define MAXIPV4ADDR 10 #define MAXIPV4ADDR 10
@ -53,113 +53,133 @@ void IRCClient::Entry()
int timer = 0; int timer = 0;
socklen_t optlen; socklen_t optlen;
while (true) { while (true)
{
if (timer > 0) { if (timer > 0)
{
timer--; timer--;
} }
switch (state) { switch (state)
case 0: {
if (terminateThread) { case 0:
printf("IRCClient::Entry: thread terminated at state=%d\n", state); if (terminateThread)
return; {
} printf("IRCClient::Entry: thread terminated at state=%d\n", state);
return;
if (timer == 0) { }
timer = 30;
if (timer == 0)
if (! ircSock.Open(host_name, AF_UNSPEC, std::to_string(port))) { {
state = 4; timer = 30;
timer = 0;
} if (! ircSock.Open(host_name, AF_UNSPEC, std::to_string(port)))
} {
break; state = 4;
timer = 0;
}
case 4: }
optlen = sizeof(int); break;
getsockopt(ircSock.GetFD(), SOL_SOCKET, SO_DOMAIN, &family, &optlen);
recvQ = new IRCMessageQueue();
sendQ = new IRCMessageQueue(); case 4:
optlen = sizeof(int);
receiver.Init(&ircSock, recvQ); getsockopt(ircSock.GetFD(), SOL_SOCKET, SO_DOMAIN, &family, &optlen);
receiver.startWork(); recvQ = new IRCMessageQueue();
sendQ = new IRCMessageQueue();
proto.setNetworkReady(true);
state = 5; receiver.Init(&ircSock, recvQ);
timer = 0; receiver.startWork();
break;
proto.setNetworkReady(true);
state = 5;
case 5: timer = 0;
if (terminateThread) { break;
state = 6;
} else {
case 5:
if (recvQ->isEOF()) { if (terminateThread)
timer = 0; {
state = 6; state = 6;
} else if (proto.processQueues(recvQ, sendQ) == false) { }
timer = 0; else
state = 6; {
}
if (recvQ->isEOF())
while ((state == 5) && sendQ->messageAvailable()) { {
IRCMessage * m = sendQ->getMessage(); timer = 0;
state = 6;
std::string out; }
else if (proto.processQueues(recvQ, sendQ) == false)
m->composeMessage(out); {
timer = 0;
char buf[200]; state = 6;
safeStringCopy(buf, out.c_str(), sizeof buf); }
int len = strlen(buf);
while ((state == 5) && sendQ->messageAvailable())
if (buf[len - 1] == 10) { // is there a NL char at the end? {
if (ircSock.Write((unsigned char *)buf, len)) { IRCMessage * m = sendQ->getMessage();
printf("IRCClient::Entry: short write\n");
std::string out;
timer = 0;
state = 6; m->composeMessage(out);
}
} else { char buf[200];
printf("IRCClient::Entry: no NL at end, len=%d\n", len); safeStringCopy(buf, out.c_str(), sizeof buf);
int len = strlen(buf);
timer = 0;
state = 6; if (buf[len - 1] == 10) // is there a NL char at the end?
} {
if (ircSock.Write((unsigned char *)buf, len))
delete m; {
} printf("IRCClient::Entry: short write\n");
}
break; timer = 0;
state = 6;
case 6: { }
if (app != NULL) { }
app->setSendQ(NULL); else
app->userListReset(); {
} printf("IRCClient::Entry: no NL at end, len=%d\n", len);
proto.setNetworkReady(false); timer = 0;
receiver.stopWork(); state = 6;
}
sleep(2);
delete m;
delete recvQ; }
delete sendQ; }
break;
ircSock.Close();
case 6:
if (terminateThread) { // request to end the thread {
printf("IRCClient::Entry: thread terminated at state=%d\n", state); if (app != NULL)
return; {
} app->setSendQ(NULL);
app->userListReset();
timer = 30; }
state = 0; // reconnect to IRC server
} proto.setNetworkReady(false);
break; receiver.stopWork();
sleep(2);
delete recvQ;
delete sendQ;
ircSock.Close();
if (terminateThread) // request to end the thread
{
printf("IRCClient::Entry: thread terminated at state=%d\n", state);
return;
}
timer = 30;
state = 0; // reconnect to IRC server
}
break;
} // switch } // switch
usleep(500000); usleep(500000);

@ -59,27 +59,32 @@ void CIRCDDB::kickWatchdog(const std::string &wdInfo)
// Send heard data, a false return implies a network error // Send heard data, a false return implies a network error
bool CIRCDDB::sendHeard(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3) bool CIRCDDB::sendHeard(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3)
{ {
if (myCall.size() != 8) { if (myCall.size() != 8)
{
printf("CIRCDDB::sendHeard:myCall: len != 8\n"); printf("CIRCDDB::sendHeard:myCall: len != 8\n");
return false; return false;
} }
if (myCallExt.size() != 4) { if (myCallExt.size() != 4)
{
printf("CIRCDDB::sendHeard:myCallExt: len != 4\n"); printf("CIRCDDB::sendHeard:myCallExt: len != 4\n");
return false; return false;
} }
if (yourCall.size() != 8) { if (yourCall.size() != 8)
{
printf("CIRCDDB::sendHeard:yourCall: len != 8\n"); printf("CIRCDDB::sendHeard:yourCall: len != 8\n");
return false; return false;
} }
if (rpt1.size() != 8) { if (rpt1.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt1: len != 8\n"); printf("CIRCDDB::sendHeard:rpt1: len != 8\n");
return false; return false;
} }
if (rpt2.size() != 8) { if (rpt2.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt2: len != 8\n"); printf("CIRCDDB::sendHeard:rpt2: len != 8\n");
return false; return false;
} }
@ -90,27 +95,32 @@ bool CIRCDDB::sendHeard(const std::string &myCall, const std::string &myCallExt,
// Send heard data, a false return implies a network error // Send heard data, a false return implies a network error
bool CIRCDDB::sendHeardWithTXMsg(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string &network_destination, const std::string &tx_message) bool CIRCDDB::sendHeardWithTXMsg(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string &network_destination, const std::string &tx_message)
{ {
if (myCall.size() != 8) { if (myCall.size() != 8)
{
printf("CIRCDDB::sendHeard:myCall: len != 8\n"); printf("CIRCDDB::sendHeard:myCall: len != 8\n");
return false; return false;
} }
if (myCallExt.size() != 4) { if (myCallExt.size() != 4)
{
printf("CIRCDDB::sendHeard:myCallExt: len != 4\n"); printf("CIRCDDB::sendHeard:myCallExt: len != 4\n");
return false; return false;
} }
if (yourCall.size() != 8) { if (yourCall.size() != 8)
{
printf("CIRCDDB::sendHeard:yourCall: len != 8\n"); printf("CIRCDDB::sendHeard:yourCall: len != 8\n");
return false; return false;
} }
if (rpt1.size() != 8) { if (rpt1.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt1: len != 8\n"); printf("CIRCDDB::sendHeard:rpt1: len != 8\n");
return false; return false;
} }
if (rpt2.size() != 8) { if (rpt2.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt2: len != 8\n"); printf("CIRCDDB::sendHeard:rpt2: len != 8\n");
return false; return false;
} }
@ -120,20 +130,26 @@ bool CIRCDDB::sendHeardWithTXMsg(const std::string &myCall, const std::string &m
if (dest.size() == 0) if (dest.size() == 0)
dest = " "; dest = " ";
if (dest.size() != 8) { if (dest.size() != 8)
{
printf("CIRCDDB::sendHeard:network_destination: len != 8\n"); printf("CIRCDDB::sendHeard:network_destination: len != 8\n");
return false; return false;
} }
std::string msg; std::string msg;
if (tx_message.length() == 20) { if (tx_message.length() == 20)
for (unsigned int i=0; i < tx_message.size(); i++) { {
for (unsigned int i=0; i < tx_message.size(); i++)
{
char ch = tx_message.at(i); char ch = tx_message.at(i);
if (ch>32 && ch<127) { if (ch>32 && ch<127)
{
msg.push_back(ch); msg.push_back(ch);
} else { }
else
{
msg.push_back('_'); msg.push_back('_');
} }
} }
@ -143,44 +159,52 @@ bool CIRCDDB::sendHeardWithTXMsg(const std::string &myCall, const std::string &m
} }
bool CIRCDDB::sendHeardWithTXStats(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1, bool CIRCDDB::sendHeardWithTXStats(const std::string &myCall, const std::string &myCallExt, const std::string &yourCall, const std::string &rpt1, const std::string &rpt2, unsigned char flag1,
unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors) unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors)
{ {
if (num_dv_frames<= 0 || num_dv_frames>65535) { if (num_dv_frames<= 0 || num_dv_frames>65535)
{
printf("CIRCDDB::sendHeard:num_dv_frames not in range 1-65535\n"); printf("CIRCDDB::sendHeard:num_dv_frames not in range 1-65535\n");
return false; return false;
} }
if (num_dv_silent_frames > num_dv_frames) { if (num_dv_silent_frames > num_dv_frames)
{
printf("CIRCDDB::sendHeard:num_dv_silent_frames > num_dv_frames\n"); printf("CIRCDDB::sendHeard:num_dv_silent_frames > num_dv_frames\n");
return false; return false;
} }
if (num_bit_errors > 4*num_dv_frames) { // max 4 bit errors per frame if (num_bit_errors > 4*num_dv_frames) // max 4 bit errors per frame
{
printf("CIRCDDB::sendHeard:num_bit_errors > (4*num_dv_frames)\n"); printf("CIRCDDB::sendHeard:num_bit_errors > (4*num_dv_frames)\n");
return false; return false;
} }
if (myCall.size() != 8) { if (myCall.size() != 8)
{
printf("CIRCDDB::sendHeard:myCall: len != 8\n"); printf("CIRCDDB::sendHeard:myCall: len != 8\n");
return false; return false;
} }
if (myCallExt.size() != 4) { if (myCallExt.size() != 4)
{
printf("CIRCDDB::sendHeard:myCallExt: len != 4\n"); printf("CIRCDDB::sendHeard:myCallExt: len != 4\n");
return false; return false;
} }
if (yourCall.size() != 8) { if (yourCall.size() != 8)
{
printf("CIRCDDB::sendHeard:yourCall: len != 8\n"); printf("CIRCDDB::sendHeard:yourCall: len != 8\n");
return false; return false;
} }
if (rpt1.size() != 8) { if (rpt1.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt1: len != 8\n"); printf("CIRCDDB::sendHeard:rpt1: len != 8\n");
return false; return false;
} }
if (rpt2.size() != 8) { if (rpt2.size() != 8)
{
printf("CIRCDDB::sendHeard:rpt2: len != 8\n"); printf("CIRCDDB::sendHeard:rpt2: len != 8\n");
return false; return false;
} }
@ -189,17 +213,23 @@ bool CIRCDDB::sendHeardWithTXStats(const std::string &myCall, const std::string
snprintf(buf, 16, "%04x", num_dv_frames); snprintf(buf, 16, "%04x", num_dv_frames);
std::string stats = buf; std::string stats = buf;
if (num_dv_silent_frames >= 0) { if (num_dv_silent_frames >= 0)
{
snprintf(buf, 16, "%02x", num_dv_silent_frames * 100 / num_dv_frames); snprintf(buf, 16, "%02x", num_dv_silent_frames * 100 / num_dv_frames);
stats.append(buf); stats.append(buf);
if (num_bit_errors >= 0) { if (num_bit_errors >= 0)
{
snprintf(buf,16, "%02x", num_bit_errors * 125 / (num_dv_frames * 3)); snprintf(buf,16, "%02x", num_bit_errors * 125 / (num_dv_frames * 3));
stats.append(buf); stats.append(buf);
} else { }
else
{
stats.append("__"); stats.append("__");
} }
} else { }
else
{
stats.append("____"); stats.append("____");
} }
@ -211,7 +241,8 @@ bool CIRCDDB::sendHeardWithTXStats(const std::string &myCall, const std::string
// Send query for a user, a false return implies a network error // Send query for a user, a false return implies a network error
bool CIRCDDB::findUser(const std::string &userCallsign) bool CIRCDDB::findUser(const std::string &userCallsign)
{ {
if (userCallsign.size() != 8) { if (userCallsign.size() != 8)
{
printf("CIRCDDB::findUser: len != 8\n"); printf("CIRCDDB::findUser: len != 8\n");
return false; return false;
} }
@ -232,24 +263,28 @@ bool CIRCDDB::receivePing(std::string &repeaterCallsign)
{ {
IRCDDB_RESPONSE_TYPE rt = app->getReplyMessageType(); IRCDDB_RESPONSE_TYPE rt = app->getReplyMessageType();
if (rt != IDRT_PING) { if (rt != IDRT_PING)
{
printf("CIRCDDB::receivePing: unexpected response type\n"); printf("CIRCDDB::receivePing: unexpected response type\n");
return false; return false;
} }
IRCMessage *m = app->getReplyMessage(); IRCMessage *m = app->getReplyMessage();
if (NULL == m) { if (NULL == m)
{
printf("CIRCDDB::receivePing: no message\n"); printf("CIRCDDB::receivePing: no message\n");
return false; return false;
} }
if (m->getCommand().compare("IDRT_PING")) { if (m->getCommand().compare("IDRT_PING"))
{
printf("CIRCDDB::receivePing: wrong messsage type\n"); printf("CIRCDDB::receivePing: wrong messsage type\n");
return false; return false;
} }
if (1 != m->getParamCount()) { if (1 != m->getParamCount())
{
printf("CIRCDDB::receivePing: unexpected number of message parameters\n"); printf("CIRCDDB::receivePing: unexpected number of message parameters\n");
return false; return false;
} }

@ -4,12 +4,14 @@
#include "../CacheManager.h" #include "../CacheManager.h"
enum IRCDDB_RESPONSE_TYPE { enum IRCDDB_RESPONSE_TYPE
{
IDRT_NONE, IDRT_NONE,
IDRT_PING IDRT_PING
}; };
enum DSTAR_PROTOCOL { enum DSTAR_PROTOCOL
{
DP_UNKNOWN, DP_UNKNOWN,
DP_DEXTRA, DP_DEXTRA,
DP_DPLUS DP_DPLUS

@ -30,7 +30,8 @@ IRCDDBApp::IRCDDBApp(const std::string &u_chan, CCacheManager *cache) : numberOf
IRCDDBApp::~IRCDDBApp() IRCDDBApp::~IRCDDBApp()
{ {
if (sendQ != NULL) { if (sendQ != NULL)
{
delete sendQ; delete sendQ;
} }
} }
@ -98,7 +99,8 @@ void IRCDDBApp::rptrQTH(const std::string &rptrcall, double latitude, double lon
void IRCDDBApp::rptrQRG(const std::string &rptrcall, double txFrequency, double duplexShift, double range, double agl) void IRCDDBApp::rptrQRG(const std::string &rptrcall, double txFrequency, double duplexShift, double range, double agl)
{ {
if (std::regex_match(rptrcall, modulePattern)) { if (std::regex_match(rptrcall, modulePattern))
{
std::string c = rptrcall; std::string c = rptrcall;
ReplaceChar(c, ' ', '_'); ReplaceChar(c, ' ', '_');
@ -119,7 +121,8 @@ void IRCDDBApp::rptrQRG(const std::string &rptrcall, double txFrequency, double
void IRCDDBApp::kickWatchdog(const std::string &s) void IRCDDBApp::kickWatchdog(const std::string &s)
{ {
if (s.length() > 0) { if (s.length() > 0)
{
std::regex nonValid("[^[:graph:]]+"); std::regex nonValid("[^[:graph:]]+");
std::smatch sm; std::smatch sm;
@ -141,13 +144,15 @@ int IRCDDBApp::getConnectionState()
IRCDDB_RESPONSE_TYPE IRCDDBApp::getReplyMessageType() IRCDDB_RESPONSE_TYPE IRCDDBApp::getReplyMessageType()
{ {
IRCMessage * m = replyQ.peekFirst(); IRCMessage * m = replyQ.peekFirst();
if (m == NULL) { if (m == NULL)
{
return IDRT_NONE; return IDRT_NONE;
} }
std::string msgType = m->getCommand(); std::string msgType = m->getCommand();
if (msgType == std::string("IDRT_PING")) { if (msgType == std::string("IDRT_PING"))
{
return IDRT_PING; return IDRT_PING;
} }
@ -181,7 +186,8 @@ void IRCDDBApp::stopWork()
void IRCDDBApp::userJoin(const std::string &nick, const std::string &name, const std::string &addr) void IRCDDBApp::userJoin(const std::string &nick, const std::string &name, const std::string &addr)
{ {
if (0 == nick.compare(0, 2, "u-")) { if (0 == nick.compare(0, 2, "u-"))
{
return; return;
} }
std::string gate(name); std::string gate(name);
@ -194,7 +200,8 @@ void IRCDDBApp::userJoin(const std::string &nick, const std::string &name, const
void IRCDDBApp::userLeave(const std::string &nick) void IRCDDBApp::userLeave(const std::string &nick)
{ {
if (0 == nick.compare(0, 2, "s-")) { if (0 == nick.compare(0, 2, "s-"))
{
currentServer.clear(); currentServer.clear();
state = 2; state = 2;
timer = 200; timer = 200;
@ -203,7 +210,8 @@ void IRCDDBApp::userLeave(const std::string &nick)
} }
std::string name(nick); std::string name(nick);
name.pop_back(); name.pop_back();
if ('-' == name.back()) { if ('-' == name.back())
{
name.pop_back(); name.pop_back();
cache->eraseName(name); cache->eraseName(name);
ToUpper(name); ToUpper(name);
@ -252,7 +260,8 @@ void IRCDDBApp::sendPing(const std::string &to, const std::string &from)
auto nick = cache->findNameNick(name); auto nick = cache->findNameNick(name);
if (! nick.empty()) { if (! nick.empty())
{
std::string rptr(from); std::string rptr(from);
ReplaceChar(rptr, ' ', '_'); ReplaceChar(rptr, ' ', '_');
IRCMessage *m = new IRCMessage(nick, "IDRT_PING"); IRCMessage *m = new IRCMessage(nick, "IDRT_PING");
@ -292,7 +301,8 @@ bool IRCDDBApp::sendHeard(const std::string &myCall, const std::string &myCallEx
std::string srv = currentServer; std::string srv = currentServer;
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if ((srv.length() > 0) && (state >= 6) && (q != NULL)) { if ((srv.length() > 0) && (state >= 6) && (q != NULL))
{
std::string cmd("UPDATE "); std::string cmd("UPDATE ");
cmd.append(getCurrentTime()); cmd.append(getCurrentTime());
@ -303,7 +313,8 @@ bool IRCDDBApp::sendHeard(const std::string &myCall, const std::string &myCallEx
cmd.append(" "); cmd.append(" ");
cmd.append(r1); cmd.append(r1);
cmd.append(" "); cmd.append(" ");
if (!statsMsg) { if (!statsMsg)
{
cmd.append("0 "); cmd.append("0 ");
} }
cmd.append(r2); cmd.append(r2);
@ -318,14 +329,18 @@ bool IRCDDBApp::sendHeard(const std::string &myCall, const std::string &myCallEx
cmd.append(" "); cmd.append(" ");
cmd.append(myext); cmd.append(myext);
if (statsMsg) { if (statsMsg)
{
cmd.append(" # "); cmd.append(" # ");
cmd.append(tx_stats); cmd.append(tx_stats);
} else { }
else
{
cmd.append(" 00 "); cmd.append(" 00 ");
cmd.append(dest); cmd.append(dest);
if (tx_msg.length() == 20) { if (tx_msg.length() == 20)
{
cmd.append(" "); cmd.append(" ");
cmd.append(tx_msg); cmd.append(tx_msg);
} }
@ -336,7 +351,8 @@ bool IRCDDBApp::sendHeard(const std::string &myCall, const std::string &myCallEx
q->putMessage(m); q->putMessage(m);
return true; return true;
} else }
else
return false; return false;
} }
@ -345,7 +361,8 @@ bool IRCDDBApp::findUser(const std::string &usrCall)
std::string srv = currentServer; std::string srv = currentServer;
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if ((srv.length() > 0) && (state >= 6) && (q != NULL)) { if ((srv.length() > 0) && (state >= 6) && (q != NULL))
{
std::string usr = usrCall; std::string usr = usrCall;
ReplaceChar(usr, ' ', '_'); ReplaceChar(usr, ' ', '_');
@ -360,7 +377,8 @@ bool IRCDDBApp::findUser(const std::string &usrCall)
void IRCDDBApp::msgChannel(IRCMessage *m) void IRCDDBApp::msgChannel(IRCMessage *m)
{ {
if (0==m->getPrefixNick().compare(0, 2, "s-") && (m->numParams >= 2)) { // server msg if (0==m->getPrefixNick().compare(0, 2, "s-") && (m->numParams >= 2)) // server msg
{
doUpdate(m->params[1]); doUpdate(m->params[1]);
} }
} }
@ -378,10 +396,12 @@ void IRCDDBApp::doNotFound(std::string &msg, std::string &retval)
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
if (std::regex_match(tk, tablePattern)) { if (std::regex_match(tk, tablePattern))
{
long tableID = std::stol(tk); long tableID = std::stol(tk);
if ((tableID < 0) || (tableID >= numberOfTables)) { if ((tableID < 0) || (tableID >= numberOfTables))
{
printf("invalid table ID %ld", tableID); printf("invalid table ID %ld", tableID);
return; return;
} }
@ -393,7 +413,8 @@ void IRCDDBApp::doNotFound(std::string &msg, std::string &retval)
tk.erase(tk.begin()); tk.erase(tk.begin());
} }
if (tableID == 0) { if (tableID == 0)
{
if (! std::regex_match(tk, dbPattern)) if (! std::regex_match(tk, dbPattern))
return; // no valid key return; // no valid key
@ -413,9 +434,11 @@ void IRCDDBApp::doUpdate(std::string &msg)
std::string tk = tkz.front(); std::string tk = tkz.front();
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
if (std::regex_match(tk, tablePattern)) { if (std::regex_match(tk, tablePattern))
{
tableID = stol(tk); tableID = stol(tk);
if ((tableID < 0) || (tableID >= numberOfTables)) { if ((tableID < 0) || (tableID >= numberOfTables))
{
printf("invalid table ID %d", tableID); printf("invalid table ID %d", tableID);
return; return;
} }
@ -427,7 +450,8 @@ void IRCDDBApp::doUpdate(std::string &msg)
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
} }
if (std::regex_match(tk, datePattern)) { if (std::regex_match(tk, datePattern))
{
if (0 == tkz.size()) if (0 == tkz.size())
return; // nothing after date string return; // nothing after date string
@ -440,7 +464,8 @@ void IRCDDBApp::doUpdate(std::string &msg)
std::string tstr(std::string(tk + " " + timeToken)); // used to update user time std::string tstr(std::string(tk + " " + timeToken)); // used to update user time
auto rtime = parseTime(tstr); // used to update maxTime for sendlist auto rtime = parseTime(tstr); // used to update maxTime for sendlist
if ((tableID == 0) || (tableID == 1)) { if ((tableID == 0) || (tableID == 1))
{
if (0 == tkz.size()) if (0 == tkz.size())
return; // nothing after time string return; // nothing after time string
@ -461,9 +486,11 @@ void IRCDDBApp::doUpdate(std::string &msg)
//printf("TABLE %d %s %s\n", tableID, key.c_str(), value.c_str()); //printf("TABLE %d %s %s\n", tableID, key.c_str(), value.c_str());
if (tableID == 1) { if (tableID == 1)
{
if (initReady && key.compare(0,6, value, 0, 6)) { if (initReady && key.compare(0,6, value, 0, 6))
{
std::string rptr(key); std::string rptr(key);
std::string gate(value); std::string gate(value);
@ -474,7 +501,9 @@ void IRCDDBApp::doUpdate(std::string &msg)
if (rtime > maxTime) if (rtime > maxTime)
maxTime = rtime; maxTime = rtime;
} }
} else if ((tableID == 0) && initReady) { }
else if ((tableID == 0) && initReady)
{
std::string user(key); std::string user(key);
std::string rptr(value); std::string rptr(value);
@ -490,15 +519,23 @@ void IRCDDBApp::doUpdate(std::string &msg)
std::string IRCDDBApp::getTableIDString(int tableID, bool spaceBeforeNumber) std::string IRCDDBApp::getTableIDString(int tableID, bool spaceBeforeNumber)
{ {
if (tableID == 0) { if (tableID == 0)
{
return std::string(""); return std::string("");
} else if ((tableID > 0) && (tableID < numberOfTables)) { }
if (spaceBeforeNumber) { else if ((tableID > 0) && (tableID < numberOfTables))
{
if (spaceBeforeNumber)
{
return std::string(" ") + std::to_string(tableID); return std::string(" ") + std::to_string(tableID);
} else { }
else
{
return std::to_string(tableID) + std::string(" "); return std::to_string(tableID) + std::string(" ");
} }
} else { }
else
{
return std::string(" TABLE_ID_OUT_OF_RANGE "); return std::string(" TABLE_ID_OUT_OF_RANGE ");
} }
} }
@ -506,7 +543,8 @@ std::string IRCDDBApp::getTableIDString(int tableID, bool spaceBeforeNumber)
void IRCDDBApp::msgQuery(IRCMessage *m) void IRCDDBApp::msgQuery(IRCMessage *m)
{ {
if (0 == strcmp(m->getPrefixNick().substr(0,2).c_str(), "s-") && (m->numParams >= 2)) { // server msg if (0 == strcmp(m->getPrefixNick().substr(0,2).c_str(), "s-") && (m->numParams >= 2)) // server msg
{
std::string msg = m->params[1]; std::string msg = m->params[1];
std::vector<std::string> tkz = stringTokenizer(msg); std::vector<std::string> tkz = stringTokenizer(msg);
@ -516,27 +554,38 @@ void IRCDDBApp::msgQuery(IRCMessage *m)
std::string cmd = tkz.front(); std::string cmd = tkz.front();
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
if (cmd == std::string("UPDATE")) { if (cmd == std::string("UPDATE"))
{
std::string restOfLine; std::string restOfLine;
while (tkz.size()) { while (tkz.size())
{
restOfLine += tkz.front(); restOfLine += tkz.front();
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
if (tkz.size()) if (tkz.size())
restOfLine += " "; restOfLine += " ";
} }
doUpdate(restOfLine); doUpdate(restOfLine);
} else if (cmd == std::string("LIST_END")) { }
if (state == 5) { // if in sendlist processing state else if (cmd == std::string("LIST_END"))
{
if (state == 5) // if in sendlist processing state
{
state = 3; // get next table state = 3; // get next table
} }
} else if (cmd == std::string("LIST_MORE")) { }
if (state == 5) { // if in sendlist processing state else if (cmd == std::string("LIST_MORE"))
{
if (state == 5) // if in sendlist processing state
{
state = 4; // send next SENDLIST state = 4; // send next SENDLIST
} }
} else if (cmd == std::string("NOT_FOUND")) { }
else if (cmd == std::string("NOT_FOUND"))
{
std::string callsign; std::string callsign;
std::string restOfLine; std::string restOfLine;
while (tkz.size()) { while (tkz.size())
{
restOfLine += tkz.front(); restOfLine += tkz.front();
tkz.erase(tkz.begin()); tkz.erase(tkz.begin());
if (tkz.size()) if (tkz.size())
@ -544,7 +593,8 @@ void IRCDDBApp::msgQuery(IRCMessage *m)
} }
doNotFound(restOfLine, callsign); doNotFound(restOfLine, callsign);
if (callsign.length() > 0) { if (callsign.length() > 0)
{
ReplaceChar(callsign, '_', ' '); ReplaceChar(callsign, '_', ' ');
IRCMessage *m2 = new IRCMessage("IDRT_USER"); IRCMessage *m2 = new IRCMessage("IDRT_USER");
@ -571,7 +621,8 @@ IRCMessageQueue *IRCDDBApp::getSendQ()
std::string IRCDDBApp::getLastEntryTime(int tableID) std::string IRCDDBApp::getLastEntryTime(int tableID)
{ {
if (tableID == 1) { if (tableID == 1)
{
struct tm *ptm = gmtime(&maxTime); struct tm *ptm = gmtime(&maxTime);
char tstr[80]; char tstr[80];
strftime(tstr, 80, "%Y-%m-%d %H:%M:%S", ptm); strftime(tstr, 80, "%Y-%m-%d %H:%M:%S", ptm);
@ -586,16 +637,20 @@ void IRCDDBApp::Entry()
{ {
int sendlistTableID = 0; int sendlistTableID = 0;
while (!terminateThread) { while (!terminateThread)
{
if (timer > 0) { if (timer > 0)
{
timer--; timer--;
} }
switch(state) { switch(state)
{
case 0: // wait for network to start case 0: // wait for network to start
if (getSendQ() != NULL) { if (getSendQ() != NULL)
{
state = 1; state = 1;
} }
break; break;
@ -608,21 +663,28 @@ void IRCDDBApp::Entry()
case 2: // choose server case 2: // choose server
printf("IRCDDBApp: state=2 choose new 's-'-user\n"); printf("IRCDDBApp: state=2 choose new 's-'-user\n");
if (getSendQ() == NULL) { if (getSendQ() == NULL)
{
state = 10; state = 10;
} else { }
if (findServerUser()) { else
{
if (findServerUser())
{
sendlistTableID = numberOfTables; sendlistTableID = numberOfTables;
state = 3; // next: send "SENDLIST" state = 3; // next: send "SENDLIST"
} else if (timer == 0) { }
else if (timer == 0)
{
state = 10; state = 10;
IRCMessage *m = new IRCMessage("QUIT"); IRCMessage *m = new IRCMessage("QUIT");
m->addParam("no op user with 's-' found."); m->addParam("no op user with 's-' found.");
IRCMessageQueue * q = getSendQ(); IRCMessageQueue * q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
} }
@ -630,13 +692,19 @@ void IRCDDBApp::Entry()
break; break;
case 3: case 3:
if (getSendQ() == NULL) { if (getSendQ() == NULL)
{
state = 10; // disconnect DB state = 10; // disconnect DB
} else { }
else
{
sendlistTableID --; sendlistTableID --;
if (sendlistTableID < 0) { if (sendlistTableID < 0)
{
state = 6; // end of sendlist state = 6; // end of sendlist
} else { }
else
{
printf("IRCDDBApp: state=3 tableID=%d\n", sendlistTableID); printf("IRCDDBApp: state=3 tableID=%d\n", sendlistTableID);
state = 4; // send "SENDLIST" state = 4; // send "SENDLIST"
timer = 900; // 15 minutes max for update timer = 900; // 15 minutes max for update
@ -645,34 +713,43 @@ void IRCDDBApp::Entry()
break; break;
case 4: case 4:
if (getSendQ() == NULL) { if (getSendQ() == NULL)
{
state = 10; // disconnect DB state = 10; // disconnect DB
} else { }
if (1 == sendlistTableID) { else
{
if (1 == sendlistTableID)
{
IRCMessage *m = new IRCMessage(currentServer, std::string("SENDLIST") + getTableIDString(sendlistTableID, true) IRCMessage *m = new IRCMessage(currentServer, std::string("SENDLIST") + getTableIDString(sendlistTableID, true)
+ std::string(" ") + getLastEntryTime(sendlistTableID)); + std::string(" ") + getLastEntryTime(sendlistTableID));
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if (q != NULL) if (q != NULL)
q->putMessage(m); q->putMessage(m);
state = 5; // wait for answers state = 5; // wait for answers
} else }
else
state = 3; // don't send SENDLIST for this table (tableID 0), go to next table state = 3; // don't send SENDLIST for this table (tableID 0), go to next table
} }
break; break;
case 5: // sendlist processing case 5: // sendlist processing
if (getSendQ() == NULL) { if (getSendQ() == NULL)
{
state = 10; // disconnect DB state = 10; // disconnect DB
} else if (timer == 0) { }
else if (timer == 0)
{
state = 10; // disconnect DB state = 10; // disconnect DB
IRCMessage *m = new IRCMessage("QUIT"); IRCMessage *m = new IRCMessage("QUIT");
m->addParam("timeout SENDLIST"); m->addParam("timeout SENDLIST");
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
@ -680,9 +757,12 @@ void IRCDDBApp::Entry()
break; break;
case 6: case 6:
if (getSendQ() == NULL) { if (getSendQ() == NULL)
{
state = 10; // disconnect DB state = 10; // disconnect DB
} else { }
else
{
printf("IRCDDBApp: state=6 initialization completed\n"); printf("IRCDDBApp: state=6 initialization completed\n");
infoTimer = 2; infoTimer = 2;
@ -697,48 +777,58 @@ void IRCDDBApp::Entry()
if (getSendQ() == NULL) if (getSendQ() == NULL)
state = 10; // disconnect DB state = 10; // disconnect DB
if (infoTimer > 0) { if (infoTimer > 0)
{
infoTimer--; infoTimer--;
if (infoTimer == 0) { if (infoTimer == 0)
{
moduleMapMutex.lock(); moduleMapMutex.lock();
for (auto itl = locationMap.begin(); itl != locationMap.end(); itl++) { for (auto itl = locationMap.begin(); itl != locationMap.end(); itl++)
{
std::string value = itl->second; std::string value = itl->second;
IRCMessage *m = new IRCMessage(currentServer, std::string("IRCDDB RPTRQTH: ") + value); IRCMessage *m = new IRCMessage(currentServer, std::string("IRCDDB RPTRQTH: ") + value);
IRCMessageQueue * q = getSendQ(); IRCMessageQueue * q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
} }
for (auto itu = urlMap.begin(); itu != urlMap.end(); itu++) { for (auto itu = urlMap.begin(); itu != urlMap.end(); itu++)
{
std::string value = itu->second; std::string value = itu->second;
IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRURL: ") + value); IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRURL: ") + value);
IRCMessageQueue * q = getSendQ(); IRCMessageQueue * q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
} }
for(auto itm = moduleMap.begin(); itm != moduleMap.end(); itm++) { for(auto itm = moduleMap.begin(); itm != moduleMap.end(); itm++)
{
std::string value = itm->second; std::string value = itm->second;
IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRQRG: ") + value); IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRQRG: ") + value);
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
} }
for(auto its = swMap.begin(); its != swMap.end(); its++) { for(auto its = swMap.begin(); its != swMap.end(); its++)
{
std::string value = its->second; std::string value = its->second;
IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRSW: ") + value); IRCMessage * m = new IRCMessage(currentServer, std::string("IRCDDB RPTRSW: ") + value);
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if (q != NULL) { if (q != NULL)
{
q->putMessage(m); q->putMessage(m);
} }
} }
@ -747,13 +837,15 @@ void IRCDDBApp::Entry()
} }
} }
if (wdTimer > 0) { if (wdTimer > 0)
{
wdTimer--; wdTimer--;
if (wdTimer <= 0) { if (wdTimer <= 0)
{
wdTimer = 900; // 15 minutes wdTimer = 900; // 15 minutes
IRCMessage *m = new IRCMessage(currentServer, std::string("IRCDDB WATCHDOG: ") + IRCMessage *m = new IRCMessage(currentServer, std::string("IRCDDB WATCHDOG: ") +
getCurrentTime() + std::string(" ") + wdInfo + std::string(" 1")); getCurrentTime() + std::string(" ") + wdInfo + std::string(" 1"));
IRCMessageQueue *q = getSendQ(); IRCMessageQueue *q = getSendQ();
if (q != NULL) if (q != NULL)

@ -56,16 +56,19 @@ void IRCMessage::parsePrefix()
{ {
unsigned int i; unsigned int i;
for (i=0; i < 3; i++) { for (i=0; i < 3; i++)
{
prefixComponents.push_back(""); prefixComponents.push_back("");
} }
int state = 0; int state = 0;
for (i=0; i < prefix.length(); i++) { for (i=0; i < prefix.length(); i++)
{
char c = prefix.at(i); char c = prefix.at(i);
switch (c) { switch (c)
{
case '!': case '!':
state = 1; // next is name state = 1; // next is name
break; break;
@ -85,7 +88,8 @@ void IRCMessage::parsePrefix()
std::string &IRCMessage::getPrefixNick() std::string &IRCMessage::getPrefixNick()
{ {
if (!prefixParsed) { if (!prefixParsed)
{
parsePrefix(); parsePrefix();
} }
@ -94,7 +98,8 @@ std::string &IRCMessage::getPrefixNick()
std::string &IRCMessage::getPrefixName() std::string &IRCMessage::getPrefixName()
{ {
if (!prefixParsed) { if (!prefixParsed)
{
parsePrefix(); parsePrefix();
} }
@ -103,7 +108,8 @@ std::string &IRCMessage::getPrefixName()
std::string &IRCMessage::getPrefixHost() std::string &IRCMessage::getPrefixHost()
{ {
if (!prefixParsed) { if (!prefixParsed)
{
parsePrefix(); parsePrefix();
} }
@ -114,16 +120,21 @@ void IRCMessage::composeMessage(std::string &output)
{ {
std::string o; std::string o;
if (prefix.length() > 0) { if (prefix.length() > 0)
{
o = std::string(":") + prefix + ' '; o = std::string(":") + prefix + ' ';
} }
o += command; o += command;
for (int i=0; i < numParams; i++) { for (int i=0; i < numParams; i++)
if (i == (numParams - 1)) { {
if (i == (numParams - 1))
{
o += (std::string(" :") + params[i]); o += (std::string(" :") + params[i]);
} else { }
else
{
o += (std::string(" ") + params[i]); o += (std::string(" ") + params[i]);
} }
} }

@ -31,7 +31,8 @@ IRCMessageQueue::IRCMessageQueue()
IRCMessageQueue::~IRCMessageQueue() IRCMessageQueue::~IRCMessageQueue()
{ {
accessMutex.lock(); accessMutex.lock();
while (! m_queue.empty()) { while (! m_queue.empty())
{
delete m_queue.front(); delete m_queue.front();
m_queue.pop(); m_queue.pop();
} }
@ -50,10 +51,10 @@ void IRCMessageQueue::signalEOF()
bool IRCMessageQueue::messageAvailable() bool IRCMessageQueue::messageAvailable()
{ {
accessMutex.lock(); accessMutex.lock();
bool retv = ! m_queue.empty(); bool retv = ! m_queue.empty();
accessMutex.unlock(); accessMutex.unlock();
return retv; return retv;
} }
IRCMessage *IRCMessageQueue::peekFirst() IRCMessage *IRCMessageQueue::peekFirst()

@ -20,7 +20,8 @@ void IRCProtocol::Init(IRCDDBApp *app, const std::string &callsign, const std::s
this->versionInfo = "CIRCDDB:"; this->versionInfo = "CIRCDDB:";
this->versionInfo.append(CIRCDDB_VERSION); this->versionInfo.append(CIRCDDB_VERSION);
if (versionInfo.length() > 0) { if (versionInfo.length() > 0)
{
this->versionInfo.append(" "); this->versionInfo.append(" ");
this->versionInfo.append(versionInfo); this->versionInfo.append(versionInfo);
} }
@ -28,7 +29,8 @@ void IRCProtocol::Init(IRCDDBApp *app, const std::string &callsign, const std::s
int hyphenPos = callsign.find('-'); int hyphenPos = callsign.find('-');
if (hyphenPos < 0) { if (hyphenPos < 0)
{
std::string n; std::string n;
n = callsign + "-1"; n = callsign + "-1";
@ -39,7 +41,9 @@ void IRCProtocol::Init(IRCDDBApp *app, const std::string &callsign, const std::s
nicks.push_back(n); nicks.push_back(n);
n = callsign + "-4"; n = callsign + "-4";
nicks.push_back(n); nicks.push_back(n);
} else { }
else
{
nicks.push_back(callsign); nicks.push_back(callsign);
} }
@ -66,13 +70,16 @@ void IRCProtocol::chooseNewNick()
void IRCProtocol::setNetworkReady(bool b) void IRCProtocol::setNetworkReady(bool b)
{ {
if (b == true) { if (b == true)
{
if (state != 0) if (state != 0)
printf("IRCProtocol::setNetworkReady: unexpected state"); printf("IRCProtocol::setNetworkReady: unexpected state");
state = 1; state = 1;
chooseNewNick(); chooseNewNick();
} else { }
else
{
state = 0; state = 0;
} }
} }
@ -80,16 +87,19 @@ void IRCProtocol::setNetworkReady(bool b)
bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ) bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
{ {
if (timer > 0) { if (timer > 0)
{
timer--; timer--;
} }
while (recvQ->messageAvailable()) { while (recvQ->messageAvailable())
{
IRCMessage *m = recvQ->getMessage(); IRCMessage *m = recvQ->getMessage();
#if defined(DEBUG_IRC) #if defined(DEBUG_IRC)
std::string d = std::string("R [") + m->prefix + std::string("] [") + m->command + std::string("]"); std::string d = std::string("R [") + m->prefix + std::string("] [") + m->command + std::string("]");
for (int i=0; i < m->numParams; i++) { for (int i=0; i < m->numParams; i++)
{
d.append(std::string(" [") + m->params[i] + std::string("]") ); d.append(std::string(" [") + m->params[i] + std::string("]") );
} }
// d.Replace(std::string("%"), std::string("%%"), true); // d.Replace(std::string("%"), std::string("%%"), true);
@ -97,96 +107,146 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
printf("%s\n", d.c_str()); printf("%s\n", d.c_str());
#endif #endif
if (0 == m->command.compare("004")) { if (0 == m->command.compare("004"))
if (state == 4) { {
if (m->params.size() > 1) { if (state == 4)
{
if (m->params.size() > 1)
{
std::regex serverNamePattern("^grp[1-9]s[1-9].ircDDB$"); std::regex serverNamePattern("^grp[1-9]s[1-9].ircDDB$");
if (std::regex_match(m->params[1], serverNamePattern)) { if (std::regex_match(m->params[1], serverNamePattern))
{
app->setBestServer(std::string("s-") + m->params[1].substr(0,6)); app->setBestServer(std::string("s-") + m->params[1].substr(0,6));
} }
} }
state = 5; // next: JOIN state = 5; // next: JOIN
app->setCurrentNick(currentNick); app->setCurrentNick(currentNick);
} }
} else if (0 == m->command.compare("PING")) { }
else if (0 == m->command.compare("PING"))
{
IRCMessage *m2 = new IRCMessage(); IRCMessage *m2 = new IRCMessage();
m2->command = "PONG"; m2->command = "PONG";
if (m->params.size() > 0) { if (m->params.size() > 0)
{
m2->numParams = 1; m2->numParams = 1;
m2->params.push_back( m->params[0] ); m2->params.push_back( m->params[0] );
} }
sendQ -> putMessage(m2); sendQ -> putMessage(m2);
} else if (0 == m->command.compare("JOIN")) { }
if ((m->numParams >= 1) && 0==m->params[0].compare(channel)) { else if (0 == m->command.compare("JOIN"))
if (0==m->getPrefixNick().compare(currentNick) && (state == 6)) { {
if (debugChannel.length() > 0) { if ((m->numParams >= 1) && 0==m->params[0].compare(channel))
{
if (0==m->getPrefixNick().compare(currentNick) && (state == 6))
{
if (debugChannel.length() > 0)
{
state = 7; // next: join debug_channel state = 7; // next: join debug_channel
} else { }
else
{
state = 10; // next: WHO * state = 10; // next: WHO *
} }
} else if (app != NULL) { }
else if (app != NULL)
{
app->userJoin( m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost()); app->userJoin( m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost());
} }
} }
if ((m->numParams >= 1) && 0==m->params[0].compare(debugChannel)) { if ((m->numParams >= 1) && 0==m->params[0].compare(debugChannel))
if (0==m->getPrefixNick().compare(currentNick) && (state == 8)) { {
if (0==m->getPrefixNick().compare(currentNick) && (state == 8))
{
state = 10; // next: WHO * state = 10; // next: WHO *
} }
} }
} else if (0 == m->command.compare("PONG")) { }
if (state == 12) { else if (0 == m->command.compare("PONG"))
{
if (state == 12)
{
timer = pingTimer; timer = pingTimer;
state = 11; state = 11;
} }
} else if (0 == m->command.compare("PART")) { }
if ((m->numParams >= 1) && 0==m->params[0].compare(channel)) { else if (0 == m->command.compare("PART"))
if (app != NULL) { {
if ((m->numParams >= 1) && 0==m->params[0].compare(channel))
{
if (app != NULL)
{
app->userLeave( m->getPrefixNick() ); app->userLeave( m->getPrefixNick() );
} }
} }
} else if (0 == m->command.compare("KICK")) { }
if ((m->numParams >= 2) && 0==m->params[0].compare(channel)) { else if (0 == m->command.compare("KICK"))
if (0 == m->params[1].compare(currentNick)) { {
if ((m->numParams >= 2) && 0==m->params[0].compare(channel))
{
if (0 == m->params[1].compare(currentNick))
{
// i was kicked!! // i was kicked!!
delete m; delete m;
return false; return false;
} else if (app != NULL) { }
else if (app != NULL)
{
app->userLeave( m->params[1] ); app->userLeave( m->params[1] );
} }
} }
} else if (0 == m->command.compare("QUIT")) { }
if (app != NULL) { else if (0 == m->command.compare("QUIT"))
{
if (app != NULL)
{
app->userLeave( m->getPrefixNick() ); app->userLeave( m->getPrefixNick() );
} }
} else if (0 == m->command.compare("PRIVMSG")) { }
if (app) { else if (0 == m->command.compare("PRIVMSG"))
{
if (app)
{
// std::string out; // std::string out;
// m->composeMessage(out); // m->composeMessage(out);
// out.pop_back(); out.pop_back(); // out.pop_back(); out.pop_back();
if (2 == m->numParams) { if (2 == m->numParams)
if (0 == m->params[0].compare(channel)) { {
if (0 == m->params[0].compare(channel))
{
app->msgChannel(m); app->msgChannel(m);
} else if (0 == m->params[0].compare(currentNick)) { }
if (0 == m->params[1].find("IDRT_PING")) { else if (0 == m->params[0].compare(currentNick))
{
if (0 == m->params[1].find("IDRT_PING"))
{
std::string from = m->params[1].substr(10); std::string from = m->params[1].substr(10);
IRCMessage *rm = new IRCMessage("IDRT_PING"); IRCMessage *rm = new IRCMessage("IDRT_PING");
rm->addParam(from); rm->addParam(from);
app->putReplyMessage(rm); app->putReplyMessage(rm);
} else }
else
app->msgQuery(m); app->msgQuery(m);
} }
} }
} }
} else if (0 == m->command.compare("352")) { // WHO list }
if ((m->numParams >= 7) && 0==m->params[0].compare(currentNick) && 0==m->params[1].compare(channel)) { else if (0 == m->command.compare("352")) // WHO list
if (app != NULL) { {
if ((m->numParams >= 7) && 0==m->params[0].compare(currentNick) && 0==m->params[1].compare(channel))
{
if (app != NULL)
{
app->userJoin(m->params[5], m->params[2], m->params[3]); app->userJoin(m->params[5], m->params[2], m->params[3]);
} }
} }
} else if (0 == m->command.compare("433")) { // nick collision }
if (state == 2) { else if (0 == m->command.compare("433")) // nick collision
{
if (state == 2)
{
state = 3; // nick collision, choose new nick state = 3; // nick collision, choose new nick
timer = 10; // wait 5 seconds.. timer = 10; // wait 5 seconds..
} }
@ -197,7 +257,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
IRCMessage * m; IRCMessage * m;
switch (state) { switch (state)
{
case 1: case 1:
m = new IRCMessage(); m = new IRCMessage();
m->command = "PASS"; m->command = "PASS";
@ -216,7 +277,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 2: case 2:
if (timer == 0) { if (timer == 0)
{
m = new IRCMessage(); m = new IRCMessage();
m->command = "USER"; m->command = "USER";
m->numParams = 4; m->numParams = 4;
@ -232,7 +294,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 3: case 3:
if (timer == 0) { if (timer == 0)
{
chooseNewNick(); chooseNewNick();
m = new IRCMessage(); m = new IRCMessage();
m->command = "NICK"; m->command = "NICK";
@ -246,7 +309,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 4: case 4:
if (timer == 0) { if (timer == 0)
{
// no login message received -> disconnect // no login message received -> disconnect
return false; return false;
} }
@ -264,14 +328,16 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 6: case 6:
if (timer == 0) { if (timer == 0)
{
// no join message received -> disconnect // no join message received -> disconnect
return false; return false;
} }
break; break;
case 7: case 7:
if (debugChannel.length() == 0) { if (debugChannel.length() == 0)
{
return false; // this state cannot be processed if there is no debug_channel return false; // this state cannot be processed if there is no debug_channel
} }
@ -286,7 +352,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 8: case 8:
if (timer == 0) { if (timer == 0)
{
// no join message received -> disconnect // no join message received -> disconnect
return false; return false;
} }
@ -303,13 +370,15 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
timer = pingTimer; timer = pingTimer;
state = 11; // wait for timer and then send ping state = 11; // wait for timer and then send ping
if (app != NULL) { if (app != NULL)
{
app->setSendQ(sendQ); // this switches the application on app->setSendQ(sendQ); // this switches the application on
} }
break; break;
case 11: case 11:
if (timer == 0) { if (timer == 0)
{
m = new IRCMessage(); m = new IRCMessage();
m->command = "PING"; m->command = "PING";
m->numParams = 1; m->numParams = 1;
@ -322,7 +391,8 @@ bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
break; break;
case 12: case 12:
if (timer == 0) { if (timer == 0)
{
// no pong message received -> disconnect // no pong message received -> disconnect
return false; return false;
} }

@ -47,25 +47,34 @@ int IRCReceiver::doRead(CTCPReaderWriterClient *ircSock, char *buf, int buf_size
res = select(fd+1, &rdset, NULL, &errset, &tv); res = select(fd+1, &rdset, NULL, &errset, &tv);
if ( res < 0 ) { if ( res < 0 )
{
printf("IRCReceiver::doread: select() error.\n"); printf("IRCReceiver::doread: select() error.\n");
return -1; return -1;
} else if ( res > 0 ) { }
if (FD_ISSET(fd, &errset)) { else if ( res > 0 )
{
if (FD_ISSET(fd, &errset))
{
printf("IRCReceiver::doRead: FD_ISSET error\n"); printf("IRCReceiver::doRead: FD_ISSET error\n");
return -1; return -1;
} }
if (FD_ISSET(fd, &rdset)) { if (FD_ISSET(fd, &rdset))
{
res = ircSock->Read((unsigned char *)buf, buf_size); res = ircSock->Read((unsigned char *)buf, buf_size);
if (res < 0) { if (res < 0)
{
printf("IRCReceiver::doRead: recv error\n"); printf("IRCReceiver::doRead: recv error\n");
return -1; return -1;
} else if (res == 0) { }
else if (res == 0)
{
printf("IRCReceiver::doRead: EOF read==0\n"); printf("IRCReceiver::doRead: EOF read==0\n");
return -1; return -1;
} else }
else
return res; return res;
} }
@ -81,67 +90,93 @@ void IRCReceiver::Entry()
int i; int i;
int state = 0; int state = 0;
while (!terminateThread) { while (!terminateThread)
{
char buf[200]; char buf[200];
int r = doRead(ircSock, buf, sizeof buf); int r = doRead(ircSock, buf, sizeof buf);
if (r < 0) { if (r < 0)
{
recvQ->signalEOF(); recvQ->signalEOF();
delete m; // delete unfinished IRCMessage delete m; // delete unfinished IRCMessage
break; break;
} }
for (i=0; i<r; i++) { for (i=0; i<r; i++)
{
char b = buf[i]; char b = buf[i];
if (b > 0) { if (b > 0)
if (b == '\n') { {
if (b == '\n')
{
recvQ->putMessage(m); recvQ->putMessage(m);
m = new IRCMessage(); m = new IRCMessage();
state = 0; state = 0;
} else if (b == '\r') { }
else if (b == '\r')
{
// do nothing // do nothing
} else switch (state) { }
else switch (state)
{
case 0: case 0:
if (b == ':') { if (b == ':')
{
state = 1; // prefix state = 1; // prefix
} else if (b == ' ') { }
else if (b == ' ')
{
// do nothing // do nothing
} else { }
else
{
m->command.push_back(b); m->command.push_back(b);
state = 2; // command state = 2; // command
} }
break; break;
case 1: case 1:
if (b == ' ') { if (b == ' ')
{
state = 2; // command is next state = 2; // command is next
} else { }
else
{
m->prefix.push_back(b); m->prefix.push_back(b);
} }
break; break;
case 2: case 2:
if (b == ' ') { if (b == ' ')
{
state = 3; // params state = 3; // params
m->numParams = 1; m->numParams = 1;
m->params.push_back(""); m->params.push_back("");
} else { }
else
{
m->command.push_back(b); m->command.push_back(b);
} }
break; break;
case 3: case 3:
if (b == ' ') { if (b == ' ')
{
m->numParams++; m->numParams++;
if (m->numParams >= 15) { if (m->numParams >= 15)
{
state = 5; // ignore the rest state = 5; // ignore the rest
} }
m->params.push_back(""); m->params.push_back("");
} else if ((b == ':') && (m->params[m->numParams - 1].length() == 0)) { }
else if ((b == ':') && (m->params[m->numParams - 1].length() == 0))
{
state = 4; // rest of line is this param state = 4; // rest of line is this param
} else { }
else
{
m->params[m->numParams - 1].push_back(b); m->params[m->numParams - 1].push_back(b);
} }
break; break;

@ -22,5 +22,5 @@ private:
bool terminateThread; bool terminateThread;
int sock; int sock;
IRCMessageQueue *recvQ; IRCMessageQueue *recvQ;
std::future<void> rec_thread; std::future<void> rec_thread;
}; };

@ -28,18 +28,19 @@ time_t parseTime(const std::string str)
std::vector<std::string> stringTokenizer(const std::string &s) std::vector<std::string> stringTokenizer(const std::string &s)
{ {
std::stringstream ss(s); std::stringstream ss(s);
std::istream_iterator<std::string> it(ss); std::istream_iterator<std::string> it(ss);
std::istream_iterator<std::string> end; std::istream_iterator<std::string> end;
std::vector<std::string> result(it, end); std::vector<std::string> result(it, end);
return result; return result;
} }
void safeStringCopy (char *dest, const char *src, unsigned int buf_size) void safeStringCopy (char *dest, const char *src, unsigned int buf_size)
{ {
unsigned int i = 0; unsigned int i = 0;
while (i<(buf_size - 1) && src[i] != 0) { while (i<(buf_size - 1) && src[i] != 0)
{
dest[i] = src[i]; dest[i] = src[i];
i++; i++;
} }
@ -64,7 +65,8 @@ char *getCurrentTime(void)
void ToUpper(std::string &str) void ToUpper(std::string &str)
{ {
for (auto it=str.begin(); it!=str.end(); it++) { for (auto it=str.begin(); it!=str.end(); it++)
{
if (islower(*it)) if (islower(*it))
*it = toupper(*it); *it = toupper(*it);
} }
@ -72,7 +74,8 @@ void ToUpper(std::string &str)
void ToLower(std::string &str) void ToLower(std::string &str)
{ {
for (auto it=str.begin(); it!=str.end(); it++) { for (auto it=str.begin(); it!=str.end(); it++)
{
if (isupper(*it)) if (isupper(*it))
*it = tolower(*it); *it = tolower(*it);
} }
@ -81,7 +84,8 @@ void ToLower(std::string &str)
void ReplaceChar(std::string &str, char from, char to) void ReplaceChar(std::string &str, char from, char to)
{ {
for (auto it=str.begin(); it!=str.end(); it++) { for (auto it=str.begin(); it!=str.end(); it++)
{
if (from == *it) if (from == *it)
*it = to; *it = to;
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.