|
|
|
|
@ -64,6 +64,9 @@ extern void dstar_dv_init();
|
|
|
|
|
extern int dstar_dv_decode(const unsigned char *d, int data[3]);
|
|
|
|
|
|
|
|
|
|
static std::atomic<bool> keep_running(true);
|
|
|
|
|
static std::atomic<unsigned short> G2_COUNTER_OUT(0);
|
|
|
|
|
static unsigned short OLD_REPLY_SEQ = 0;
|
|
|
|
|
static unsigned short NEW_REPLY_SEQ = 0;
|
|
|
|
|
|
|
|
|
|
/* signal catching function */
|
|
|
|
|
static void sigCatch(int signum)
|
|
|
|
|
@ -277,11 +280,10 @@ bool CQnetGateway::read_config(char *cfgFile)
|
|
|
|
|
printf("cannot define both icom and non-icom modules\n");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (is_not_icom) {
|
|
|
|
|
if (! get_value(cfg, std::string(path+"ip").c_str(), rptr.mod[m].portip.ip, 7, IP_SIZE, "127.0.0.1"))
|
|
|
|
|
|
|
|
|
|
if (! get_value(cfg, std::string(path+"ip").c_str(), rptr.mod[m].portip.ip, 7, IP_SIZE, is_icom ? "172.16.0.20" : "127.0.0.1"))
|
|
|
|
|
return true;
|
|
|
|
|
get_value(cfg, std::string(path+"port").c_str(), rptr.mod[m].portip.port, 16000, 65535, 19998+m);
|
|
|
|
|
}
|
|
|
|
|
get_value(cfg, std::string(path+"port").c_str(), rptr.mod[m].portip.port, 16000, 65535, is_icom ? 20000 : 19998+m);
|
|
|
|
|
get_value(cfg, std::string(path+"frequency").c_str(), rptr.mod[m].frequency, 0.0, 1.0e12, 0.0);
|
|
|
|
|
get_value(cfg, std::string(path+"offset").c_str(), rptr.mod[m].offset,-1.0e12, 1.0e12, 0.0);
|
|
|
|
|
get_value(cfg, std::string(path+"range").c_str(), rptr.mod[m].range, 0.0, 1609344.0, 0.0);
|
|
|
|
|
@ -306,9 +308,31 @@ bool CQnetGateway::read_config(char *cfgFile)
|
|
|
|
|
} else
|
|
|
|
|
rptr.mod[m].defined = false;
|
|
|
|
|
}
|
|
|
|
|
if (false==rptr.mod[0].defined && false==rptr.mod[1].defined && false==rptr.mod[2].defined) {
|
|
|
|
|
if (! is_icom && ! is_not_icom) {
|
|
|
|
|
printf("No modules defined!\n");
|
|
|
|
|
return true;
|
|
|
|
|
} else if (is_icom) { // make sure all ICOM modules have the same IP and port number
|
|
|
|
|
std::string addr;
|
|
|
|
|
int port;
|
|
|
|
|
for (int i=0; i<3; i++) {
|
|
|
|
|
if (rptr.mod[i].defined) {
|
|
|
|
|
if (addr.size()) {
|
|
|
|
|
if (addr.compare(rptr.mod[i].portip.ip) || port!=rptr.mod[i].portip.port) {
|
|
|
|
|
printf("all defined ICOM modules must have the same IP and port number!\n");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
addr = rptr.mod[i].portip.ip;
|
|
|
|
|
port = rptr.mod[i].portip.port;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i=0; i<3; i++) {
|
|
|
|
|
if (! rptr.mod[i].defined) {
|
|
|
|
|
rptr.mod[i].portip.ip = addr;
|
|
|
|
|
rptr.mod[i].portip.port = port;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// gateway
|
|
|
|
|
@ -321,10 +345,10 @@ bool CQnetGateway::read_config(char *cfgFile)
|
|
|
|
|
|
|
|
|
|
get_value(cfg, path+"external.port", g2_external.port, 1024, 65535, 40000);
|
|
|
|
|
|
|
|
|
|
if (! get_value(cfg, path+"internal.ip", g2_internal.ip, 7, IP_SIZE, "0.0.0.0"))
|
|
|
|
|
if (! get_value(cfg, path+"internal.ip", g2_internal.ip, 7, IP_SIZE, is_icom ? "172.16.0.1" : "0.0.0.0"))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
get_value(cfg, path+"internal.port", g2_internal.port, 16000, 65535, 19000);
|
|
|
|
|
get_value(cfg, path+"internal.port", g2_internal.port, 16000, 65535, is_icom ? 20000 : 19000);
|
|
|
|
|
|
|
|
|
|
get_value(cfg, path+"regen_header", bool_regen_header, true);
|
|
|
|
|
|
|
|
|
|
@ -701,15 +725,34 @@ void CQnetGateway::process()
|
|
|
|
|
|
|
|
|
|
ii->kickWatchdog(IRCDDB_VERSION);
|
|
|
|
|
|
|
|
|
|
if (is_icom) { // send INIT to Icom Stack
|
|
|
|
|
unsigned char buf[10];
|
|
|
|
|
if (is_icom) {
|
|
|
|
|
// send INIT to Icom Stack
|
|
|
|
|
unsigned char buf[500];
|
|
|
|
|
memset(buf, 0, 10);
|
|
|
|
|
memcpy(buf, "INIT", 4);
|
|
|
|
|
buf[6] = 0x73U;
|
|
|
|
|
sendto(srv_sock, buf, 10, 0, (struct sockaddr *)&, sizeof(struct sockaddr_in));
|
|
|
|
|
// we can use the module a band_addr for INIT
|
|
|
|
|
sendto(srv_sock, buf, 10, 0, (struct sockaddr *)&toRptr[0].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
printf("Waiting for ICOM controller...\n");
|
|
|
|
|
|
|
|
|
|
// get the acknowledgement from the ICOM Stack
|
|
|
|
|
while (keep_running) {
|
|
|
|
|
socklen_t fromlength = sizeof(struct sockaddr_in);
|
|
|
|
|
int recvlen = recvfrom(srv_sock, buf, 500, 0, (struct sockaddr *)&fromRptr, &fromlength);
|
|
|
|
|
if (10==recvlen && 0==memcmp(buf, "INIT", 4) && 0x72U==buf[6] && 0x0U==buf[7]) {
|
|
|
|
|
OLD_REPLY_SEQ = 256U * buf[4] + buf[5];
|
|
|
|
|
NEW_REPLY_SEQ = OLD_REPLY_SEQ + 1;
|
|
|
|
|
G2_COUNTER_OUT = NEW_REPLY_SEQ;
|
|
|
|
|
unsigned int ui = G2_COUNTER_OUT;
|
|
|
|
|
printf("SYNC: old=%u, new=%u out=%u\n", OLD_REPLY_SEQ, NEW_REPLY_SEQ, ui);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
}
|
|
|
|
|
printf("Detected ICOM controller!\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (keep_running) {
|
|
|
|
|
for (int i=0; i<3; i++) {
|
|
|
|
|
/* echotest recording timed out? */
|
|
|
|
|
@ -884,7 +927,7 @@ void CQnetGateway::process()
|
|
|
|
|
g2buflen, inet_ntoa(fromDst4.sin_addr), ntohs(fromDst4.sin_port));
|
|
|
|
|
|
|
|
|
|
memcpy(rptrbuf.pkt_id, "DSTR", 4);
|
|
|
|
|
rptrbuf.counter = toRptr[i].G2_COUNTER;
|
|
|
|
|
rptrbuf.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++); // bump the counter
|
|
|
|
|
rptrbuf.flag[0] = 0x73;
|
|
|
|
|
rptrbuf.flag[1] = 0x12;
|
|
|
|
|
rptrbuf.flag[2] = 0x00;
|
|
|
|
|
@ -904,13 +947,10 @@ void CQnetGateway::process()
|
|
|
|
|
/* time it, in case stream times out */
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump the G2 counter */
|
|
|
|
|
toRptr[i].G2_COUNTER++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
} else { // g2buflen == 27
|
|
|
|
|
if (g2buf.counter & 0x40) {
|
|
|
|
|
if (bool_qso_details)
|
|
|
|
|
printf("END from g2: streamID=%04x, %d bytes from IP=%s\n", g2buf.streamid, g2buflen,inet_ntoa(fromDst4.sin_addr));
|
|
|
|
|
@ -923,7 +963,7 @@ void CQnetGateway::process()
|
|
|
|
|
if ((toRptr[i].streamid==g2buf.streamid) &&
|
|
|
|
|
(toRptr[i].adr == fromDst4.sin_addr.s_addr)) {
|
|
|
|
|
memcpy(rptrbuf.pkt_id, "DSTR", 4);
|
|
|
|
|
rptrbuf.counter = toRptr[i].G2_COUNTER;
|
|
|
|
|
rptrbuf.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++);
|
|
|
|
|
rptrbuf.flag[0] = 0x73;
|
|
|
|
|
rptrbuf.flag[1] = 0x12;
|
|
|
|
|
rptrbuf.flag[2] = 0x00;
|
|
|
|
|
@ -936,9 +976,6 @@ void CQnetGateway::process()
|
|
|
|
|
/* timeit */
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump G2 counter */
|
|
|
|
|
toRptr[i].G2_COUNTER++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
|
|
|
|
|
/* End of stream ? */
|
|
|
|
|
@ -970,18 +1007,15 @@ void CQnetGateway::process()
|
|
|
|
|
if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0)) {
|
|
|
|
|
printf("Re-generating header for streamID=%04x\n", g2buf.streamid);
|
|
|
|
|
|
|
|
|
|
toRptr[i].saved_hdr[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff);
|
|
|
|
|
toRptr[i].saved_hdr[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff);
|
|
|
|
|
toRptr[i].saved_hdr[4] = (unsigned char)(((is_icom ? G2_COUNTER_OUT : toRptr[i].G2_COUNTER) >> 8) & 0xff);
|
|
|
|
|
toRptr[i].saved_hdr[5] = (unsigned char)((is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++) & 0xff);
|
|
|
|
|
|
|
|
|
|
/* re-generate/send the header */
|
|
|
|
|
sendto(srv_sock, toRptr[i].saved_hdr, 58, 0, (struct sockaddr *)&toRptr[i].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
|
|
|
|
|
/* bump G2 counter */
|
|
|
|
|
toRptr[i].G2_COUNTER++;
|
|
|
|
|
|
|
|
|
|
/* send this audio packet to repeater */
|
|
|
|
|
memcpy(rptrbuf.pkt_id, "DSTR", 4);
|
|
|
|
|
rptrbuf.counter = toRptr[i].G2_COUNTER;
|
|
|
|
|
rptrbuf.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++);
|
|
|
|
|
rptrbuf.flag[0] = 0x73;
|
|
|
|
|
rptrbuf.flag[1] = 0x12;
|
|
|
|
|
rptrbuf.flag[2] = 0x00;
|
|
|
|
|
@ -998,9 +1032,6 @@ void CQnetGateway::process()
|
|
|
|
|
/* time it, in case stream times out */
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump the G2 counter */
|
|
|
|
|
toRptr[i].G2_COUNTER++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@ -1019,21 +1050,43 @@ void CQnetGateway::process()
|
|
|
|
|
socklen_t fromlen = sizeof(struct sockaddr_in);
|
|
|
|
|
int recvlen = recvfrom(srv_sock, rptrbuf.pkt_id, 58, 0, (struct sockaddr *)&fromRptr, &fromlen);
|
|
|
|
|
|
|
|
|
|
/* DV */
|
|
|
|
|
if ( ((recvlen == 58) || (recvlen == 29) || (recvlen == 32)) &&
|
|
|
|
|
if (0 == memcmp(rptrbuf.pkt_id, "DSTR", 4)) {
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
// some ICOM handshaking...
|
|
|
|
|
if (is_icom && 10==recvlen && 0x72==rptrbuf.flag[0]) { // ACK from rptr
|
|
|
|
|
NEW_REPLY_SEQ = ntohs(rptrbuf.counter);
|
|
|
|
|
if (NEW_REPLY_SEQ == OLD_REPLY_SEQ) {
|
|
|
|
|
G2_COUNTER_OUT = NEW_REPLY_SEQ;
|
|
|
|
|
OLD_REPLY_SEQ = NEW_REPLY_SEQ - 1;
|
|
|
|
|
} else
|
|
|
|
|
OLD_REPLY_SEQ = NEW_REPLY_SEQ;
|
|
|
|
|
} else if (is_icom && 0x73U==rptrbuf.flag[0] && (0x21U==rptrbuf.flag[1] || 0x11U==rptrbuf.flag[1] || 0x0U==rptrbuf.flag[1])) {
|
|
|
|
|
rptrbuf.flag[0] = 0x72U;
|
|
|
|
|
memset(rptrbuf.flag+1, 0x0U, 3);
|
|
|
|
|
sendto(srv_sock, rptrbuf.pkt_id, 10, 0, (struct sockaddr *)&toRptr[0].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
// end of ICOM handshaking
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
} else if ( ((recvlen == 58) || (recvlen == 29) || (recvlen == 32)) &&
|
|
|
|
|
(rptrbuf.flag[0] == 0x73) && (rptrbuf.flag[1] == 0x12) &&
|
|
|
|
|
(0 == memcmp(rptrbuf.pkt_id,"DSTR", 4)) &&
|
|
|
|
|
(rptrbuf.vpkt.icm_id == 0x20) && (rptrbuf.flag[2] == 0x00) &&
|
|
|
|
|
((rptrbuf.remaining == 0x30) || /* 48 bytes follow */
|
|
|
|
|
(rptrbuf.remaining == 0x13) || /* 19 bytes follow */
|
|
|
|
|
(rptrbuf.remaining == 0x16)) ) { /* 22 bytes follow */
|
|
|
|
|
if (is_icom) { // acknowledge packet to ICOM
|
|
|
|
|
SDSTR reply;
|
|
|
|
|
memcpy(reply.pkt_id, "DSTR", 4);
|
|
|
|
|
reply.counter = rptrbuf.counter;
|
|
|
|
|
reply.flag[0] = 0x72U;
|
|
|
|
|
memset(reply.flag+1, 0, 3);
|
|
|
|
|
sendto(srv_sock, reply.pkt_id, 10, 0, (struct sockaddr *)&toRptr[0].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (recvlen == 58) {
|
|
|
|
|
|
|
|
|
|
if (bool_qso_details)
|
|
|
|
|
printf("START from rptr: cntr=%04x, streamID=%04x, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s\n",
|
|
|
|
|
rptrbuf.counter,
|
|
|
|
|
rptrbuf.vpkt.streamid,
|
|
|
|
|
ntohs(rptrbuf.counter),
|
|
|
|
|
ntohs(rptrbuf.vpkt.streamid),
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0], rptrbuf.vpkt.hdr.flag[1], rptrbuf.vpkt.hdr.flag[2],
|
|
|
|
|
rptrbuf.vpkt.hdr.my, rptrbuf.vpkt.hdr.nm, rptrbuf.vpkt.hdr.ur,
|
|
|
|
|
rptrbuf.vpkt.hdr.r1, rptrbuf.vpkt.hdr.r2, recvlen, inet_ntoa(fromRptr.sin_addr));
|
|
|
|
|
@ -1333,6 +1386,9 @@ void CQnetGateway::process()
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump the G2 counter */
|
|
|
|
|
if (is_icom)
|
|
|
|
|
G2_COUNTER_OUT++;
|
|
|
|
|
else
|
|
|
|
|
toRptr[i].G2_COUNTER++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
@ -1344,9 +1400,7 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if ((rptrbuf.vpkt.hdr.ur[7] == '0') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[6] == 'C') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[0] == ' ')) {
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " C0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1358,23 +1412,10 @@ void CQnetGateway::process()
|
|
|
|
|
} else
|
|
|
|
|
printf("No voicemail to clear or still recording\n");
|
|
|
|
|
}
|
|
|
|
|
} else if ((rptrbuf.vpkt.hdr.ur[7] == '0') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[6] == 'R') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[0] == ' ')) {
|
|
|
|
|
int i = -1;
|
|
|
|
|
switch (rptrbuf.vpkt.hdr.r1[7]) {
|
|
|
|
|
case 'A':
|
|
|
|
|
i = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
i = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
i = 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " R0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i >= 0) {
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
/* voicemail file is closed */
|
|
|
|
|
if ((vm[i].fd == -1) && (vm[i].file[0] != '\0')) {
|
|
|
|
|
try {
|
|
|
|
|
@ -1385,9 +1426,7 @@ void CQnetGateway::process()
|
|
|
|
|
} else
|
|
|
|
|
printf("No voicemail to recall or still recording\n");
|
|
|
|
|
}
|
|
|
|
|
} else if ((rptrbuf.vpkt.hdr.ur[7] == '0') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[6] == 'S') &&
|
|
|
|
|
(rptrbuf.vpkt.hdr.ur[0] == ' ')) {
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " S0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1396,8 +1435,7 @@ void CQnetGateway::process()
|
|
|
|
|
else {
|
|
|
|
|
memset(tempfile, '\0', sizeof(tempfile));
|
|
|
|
|
snprintf(tempfile, FILENAME_MAX, "%s/%c_%s",
|
|
|
|
|
echotest_dir.c_str(),
|
|
|
|
|
rptrbuf.vpkt.hdr.r1[7],
|
|
|
|
|
echotest_dir.c_str(), rptrbuf.vpkt.hdr. r1[7],
|
|
|
|
|
"voicemail.dat");
|
|
|
|
|
|
|
|
|
|
vm[i].fd = open(tempfile,
|
|
|
|
|
@ -1408,8 +1446,7 @@ void CQnetGateway::process()
|
|
|
|
|
else {
|
|
|
|
|
strcpy(vm[i].file, tempfile);
|
|
|
|
|
printf("Recording mod %c for voicemail into file:[%s]\n",
|
|
|
|
|
rptrbuf.vpkt.hdr.r1[7],
|
|
|
|
|
vm[i].file);
|
|
|
|
|
rptrbuf.vpkt.hdr.r1[7], vm[i].file);
|
|
|
|
|
|
|
|
|
|
time(&vm[i].last_time);
|
|
|
|
|
vm[i].streamid = rptrbuf.vpkt.streamid;
|
|
|
|
|
@ -1440,7 +1477,7 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (('E' == rptrbuf.vpkt.hdr.ur[7]) && (' ' == rptrbuf.vpkt.hdr.ur[0])) {
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " E", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1532,13 +1569,16 @@ void CQnetGateway::process()
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump the G2 counter */
|
|
|
|
|
if (is_icom)
|
|
|
|
|
G2_COUNTER_OUT++;
|
|
|
|
|
else
|
|
|
|
|
toRptr[i].G2_COUNTER ++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // recvlen != 58
|
|
|
|
|
} else { // recvlen is 29 or 32
|
|
|
|
|
for (int i=0; i<3; i++) {
|
|
|
|
|
if (band_txt[i].streamID == rptrbuf.vpkt.streamid) {
|
|
|
|
|
time(&band_txt[i].last_time);
|
|
|
|
|
@ -2079,6 +2119,9 @@ void CQnetGateway::process()
|
|
|
|
|
time(&toRptr[i].last_time);
|
|
|
|
|
|
|
|
|
|
/* bump G2 counter */
|
|
|
|
|
if (is_icom)
|
|
|
|
|
G2_COUNTER_OUT++;
|
|
|
|
|
else
|
|
|
|
|
toRptr[i].G2_COUNTER ++;
|
|
|
|
|
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
@ -2095,7 +2138,8 @@ void CQnetGateway::process()
|
|
|
|
|
|
|
|
|
|
if (rptrbuf.vpkt.ctrl & 0x40) {
|
|
|
|
|
if (bool_qso_details)
|
|
|
|
|
printf("END from rptr: cntr=%04x, streamID=%04x, %d bytes\n", rptrbuf.counter, rptrbuf.vpkt.streamid, recvlen);
|
|
|
|
|
printf("END from rptr: cntr=%04x, streamID=%04x, %d bytes\n", ntohs(rptrbuf.counter), ntohs(rptrbuf.vpkt.streamid), recvlen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -2328,12 +2372,10 @@ void CQnetGateway::APRSBeaconThread()
|
|
|
|
|
|
|
|
|
|
void CQnetGateway::PlayFileThread(char *file)
|
|
|
|
|
{
|
|
|
|
|
unsigned short rlen = 0;
|
|
|
|
|
unsigned char dstar_buf[56];
|
|
|
|
|
unsigned char rptr_buf[58];
|
|
|
|
|
short int i = 0;
|
|
|
|
|
struct sigaction act;
|
|
|
|
|
SDSTR dstr;
|
|
|
|
|
SDSVT dsvt;
|
|
|
|
|
|
|
|
|
|
struct sigaction act;
|
|
|
|
|
act.sa_handler = sigCatch;
|
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
|
|
|
act.sa_flags = SA_RESTART;
|
|
|
|
|
@ -2358,14 +2400,14 @@ void CQnetGateway::PlayFileThread(char *file)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t nread = fread(dstar_buf, 10, 1, fp);
|
|
|
|
|
size_t nread = fread(dstr.pkt_id, 10, 1, fp);
|
|
|
|
|
if (nread != 1) {
|
|
|
|
|
printf("Cant read first 10 bytes in %s\n", file);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memcmp(dstar_buf, "DVTOOL", 6) != 0) {
|
|
|
|
|
if (memcmp(dstr.pkt_id, "DVTOOL", 6) != 0) {
|
|
|
|
|
printf("DVTOOL keyword not found in %s\n", file);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return;
|
|
|
|
|
@ -2373,6 +2415,7 @@ void CQnetGateway::PlayFileThread(char *file)
|
|
|
|
|
|
|
|
|
|
sleep(play_wait);
|
|
|
|
|
while (keep_running) {
|
|
|
|
|
unsigned short rlen;
|
|
|
|
|
nread = fread(&rlen, 2, 1, fp);
|
|
|
|
|
if (nread != 1)
|
|
|
|
|
break;
|
|
|
|
|
@ -2381,54 +2424,51 @@ void CQnetGateway::PlayFileThread(char *file)
|
|
|
|
|
printf("Expected 56 bytes or 27 bytes, found %d\n", rlen);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
nread = fread(dstar_buf, rlen, 1, fp);
|
|
|
|
|
nread = fread(dsvt.title, rlen, 1, fp);
|
|
|
|
|
if (nread == 1) {
|
|
|
|
|
if (memcmp(dstar_buf, "DSVT", 4) != 0) {
|
|
|
|
|
if (memcmp(dsvt.title, "DSVT", 4) != 0) {
|
|
|
|
|
printf("DVST keyword not found in %s\n", file);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dstar_buf[8] != 0x20) {
|
|
|
|
|
if (dsvt.id != 0x20) {
|
|
|
|
|
printf("Not Voice type in %s\n", file);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((dstar_buf[4] != 0x10) && (dstar_buf[4] != 0x20)) {
|
|
|
|
|
if ((dsvt.config != 0x10) && (dsvt.config != 0x20)) {
|
|
|
|
|
printf("Not a valid record type in %s\n",file);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
if (rlen == 56) {
|
|
|
|
|
/* which module is this recording for? */
|
|
|
|
|
if (dstar_buf[25] == 'A')
|
|
|
|
|
i = 0;
|
|
|
|
|
else if (dstar_buf[25] == 'B')
|
|
|
|
|
i = 1;
|
|
|
|
|
else if (dstar_buf[25] == 'C')
|
|
|
|
|
i = 2;
|
|
|
|
|
|
|
|
|
|
memcpy(rptr_buf, "DSTR", 4);
|
|
|
|
|
rptr_buf[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff);
|
|
|
|
|
rptr_buf[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff);
|
|
|
|
|
rptr_buf[6] = 0x73;
|
|
|
|
|
rptr_buf[7] = 0x12;
|
|
|
|
|
rptr_buf[8] = 0x00;
|
|
|
|
|
rptr_buf[9] = 0x30;
|
|
|
|
|
rptr_buf[10] = 0x20;
|
|
|
|
|
memcpy(rptr_buf + 11, dstar_buf + 9, 47);
|
|
|
|
|
i = dsvt.hdr.rpt1[7] - 'A';
|
|
|
|
|
if (i<0 || i>2) {
|
|
|
|
|
printf("found a bad module destination for this file!\n");
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(dstr.pkt_id, "DSTR", 4);
|
|
|
|
|
dstr.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++);
|
|
|
|
|
dstr.flag[0] = 0x73;
|
|
|
|
|
dstr.flag[1] = 0x12;
|
|
|
|
|
dstr.flag[2] = 0x00;
|
|
|
|
|
dstr.remaining = 0x30;
|
|
|
|
|
dstr.vpkt.icm_id = 0x20;
|
|
|
|
|
memcpy(&dstr.vpkt.dst_rptr_id, dsvt.flagb, 47);
|
|
|
|
|
|
|
|
|
|
/* We did not change anything */
|
|
|
|
|
// calcPFCS(rptr_buf, 58);
|
|
|
|
|
} else {
|
|
|
|
|
rptr_buf[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff);
|
|
|
|
|
rptr_buf[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff);
|
|
|
|
|
rptr_buf[9] = 0x13;
|
|
|
|
|
memcpy(rptr_buf + 11, dstar_buf + 9, 18);
|
|
|
|
|
dstr.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++);
|
|
|
|
|
dstr.remaining = 0x13;
|
|
|
|
|
memcpy(&dstr.vpkt.dst_rptr_id, dsvt.flagb, 18);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendto(srv_sock, rptr_buf, rlen + 2, 0, (struct sockaddr *)&toRptr[i].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
|
|
|
|
|
toRptr[i].G2_COUNTER ++;
|
|
|
|
|
sendto(srv_sock, dstr.pkt_id, rlen+2, 0, (struct sockaddr *)&toRptr[i].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(play_delay));
|
|
|
|
|
}
|
|
|
|
|
@ -2560,7 +2600,7 @@ int CQnetGateway::init(char *cfgfile)
|
|
|
|
|
|
|
|
|
|
// Open G2 INTERNAL:
|
|
|
|
|
// default non-icom 127.0.0.1:19000
|
|
|
|
|
// default icom 172.10.0.5:20000
|
|
|
|
|
// default icom 172.16.0.1:20000
|
|
|
|
|
srv_sock = open_port(g2_internal);
|
|
|
|
|
if (0 > srv_sock) {
|
|
|
|
|
printf("Can't open %s:%d\n", g2_internal.ip.c_str(), g2_internal.port);
|
|
|
|
|
|