|
|
|
|
@ -662,104 +662,16 @@ bool CQnetGateway::get_yrcall_rptr(char *call, char *arearp_cs, char *zonerp_cs,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* run the main loop for QnetGateway */
|
|
|
|
|
void CQnetGateway::process()
|
|
|
|
|
bool CQnetGateway::Flag_is_ok(unsigned char flag)
|
|
|
|
|
{
|
|
|
|
|
SDSVT g2buf;
|
|
|
|
|
fd_set fdset;
|
|
|
|
|
|
|
|
|
|
char temp_radio_user[CALL_SIZE + 1];
|
|
|
|
|
char temp_mod;
|
|
|
|
|
time_t t_now;
|
|
|
|
|
|
|
|
|
|
char arearp_cs[CALL_SIZE + 1];
|
|
|
|
|
char zonerp_cs[CALL_SIZE + 1];
|
|
|
|
|
char ip[IP_SIZE + 1];
|
|
|
|
|
|
|
|
|
|
char tempfile[FILENAME_MAX + 1];
|
|
|
|
|
long num_recs = 0L;
|
|
|
|
|
short int rec_len = 56;
|
|
|
|
|
|
|
|
|
|
std::future<void> aprs_future, irc_data_future;
|
|
|
|
|
|
|
|
|
|
// dtmf stuff
|
|
|
|
|
int dtmf_buf_count[3] = {0, 0, 0};
|
|
|
|
|
char dtmf_buf[3][MAX_DTMF_BUF + 1] = { {""}, {""}, {""} };
|
|
|
|
|
int dtmf_last_frame[3] = { 0, 0, 0 };
|
|
|
|
|
unsigned int dtmf_counter[3] = { 0, 0, 0 };
|
|
|
|
|
|
|
|
|
|
/* START: TEXT crap */
|
|
|
|
|
bool new_group[3] = { true, true, true };
|
|
|
|
|
int header_type = 0;
|
|
|
|
|
short to_print[3] = { 0, 0, 0 };
|
|
|
|
|
bool ABC_grp[3] = { false, false, false };
|
|
|
|
|
bool C_seen[3] = { false, false, false };
|
|
|
|
|
unsigned char tmp_txt[3];
|
|
|
|
|
/* END: TEXT crap */
|
|
|
|
|
|
|
|
|
|
int max_nfds = 0;
|
|
|
|
|
|
|
|
|
|
dstar_dv_init();
|
|
|
|
|
|
|
|
|
|
if (g2_sock > max_nfds)
|
|
|
|
|
max_nfds = g2_sock;
|
|
|
|
|
if (srv_sock > max_nfds)
|
|
|
|
|
max_nfds = srv_sock;
|
|
|
|
|
printf("g2=%d, srv=%d, MAX+1=%d\n", g2_sock, srv_sock, max_nfds + 1);
|
|
|
|
|
|
|
|
|
|
/* start the beacon thread */
|
|
|
|
|
if (bool_send_aprs) {
|
|
|
|
|
try {
|
|
|
|
|
aprs_future = std::async(std::launch::async, &CQnetGateway::APRSBeaconThread, this);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
printf("Failed to start the APRSBeaconThread. Exception: %s\n", e.what());
|
|
|
|
|
}
|
|
|
|
|
if (aprs_future.valid())
|
|
|
|
|
printf("APRS beacon thread started\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
irc_data_future = std::async(std::launch::async, &CQnetGateway::GetIRCDataThread, this);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
printf("Failed to start GetIRCDataThread. Exception: %s\n", e.what());
|
|
|
|
|
keep_running = false;
|
|
|
|
|
}
|
|
|
|
|
if (keep_running)
|
|
|
|
|
printf("get_irc_data thread started\n");
|
|
|
|
|
|
|
|
|
|
ii->kickWatchdog(IRCDDB_VERSION);
|
|
|
|
|
|
|
|
|
|
if (is_icom) {
|
|
|
|
|
// send INIT to Icom Stack
|
|
|
|
|
unsigned char buf[500];
|
|
|
|
|
memset(buf, 0, 10);
|
|
|
|
|
memcpy(buf, "INIT", 4);
|
|
|
|
|
buf[6] = 0x73U;
|
|
|
|
|
// 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));
|
|
|
|
|
// normal break emr emr+break
|
|
|
|
|
return 0x00U==flag || 0x08U==flag || 0x20U==flag || 0x28U==flag;
|
|
|
|
|
}
|
|
|
|
|
printf("Detected ICOM controller!\n");
|
|
|
|
|
} else
|
|
|
|
|
printf("Skipping ICOM initialization\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (keep_running) {
|
|
|
|
|
void CQnetGateway::ProcessTimeouts()
|
|
|
|
|
{
|
|
|
|
|
for (int i=0; i<3; i++) {
|
|
|
|
|
time_t t_now;
|
|
|
|
|
/* echotest recording timed out? */
|
|
|
|
|
if (recd[i].last_time != 0) {
|
|
|
|
|
time(&t_now);
|
|
|
|
|
@ -873,18 +785,11 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wait 20 ms max */
|
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
|
FD_SET(g2_sock, &fdset);
|
|
|
|
|
FD_SET(srv_sock, &fdset);
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
|
tv.tv_usec = 20000; /* 20 ms */
|
|
|
|
|
(void)select(max_nfds + 1, &fdset, 0, 0, &tv);
|
|
|
|
|
|
|
|
|
|
/* process packets coming from remote G2 */
|
|
|
|
|
if (FD_ISSET(g2_sock, &fdset)) {
|
|
|
|
|
void CQnetGateway::ProcessRouting()
|
|
|
|
|
{
|
|
|
|
|
SDSVT g2buf;
|
|
|
|
|
socklen_t fromlen = sizeof(struct sockaddr_in);
|
|
|
|
|
int g2buflen = recvfrom(g2_sock, g2buf.title, 56, 0, (struct sockaddr *)&fromDst4, &fromlen);
|
|
|
|
|
|
|
|
|
|
@ -899,9 +804,7 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( (g2buflen==56 || g2buflen==27) && 0==memcmp(g2buf.title, "DSVT", 4) &&
|
|
|
|
|
(g2buf.config==0x10 || g2buf.config==0x20) && g2buf.id==0x20) {
|
|
|
|
|
if ( (g2buflen==56 || g2buflen==27) && 0==memcmp(g2buf.title, "DSVT", 4) && (g2buf.config==0x10 || g2buf.config==0x20) && g2buf.id==0x20) {
|
|
|
|
|
if (g2buflen == 56) {
|
|
|
|
|
|
|
|
|
|
// Find out the local repeater module IP/port to send the data to
|
|
|
|
|
@ -911,17 +814,9 @@ void CQnetGateway::process()
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
// toRptr[i] is active if a remote system is talking to it or
|
|
|
|
|
// toRptr[i] is receiving data from a cross-band
|
|
|
|
|
if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0) &&
|
|
|
|
|
((g2buf.hdr.flag[0] == 0x00) ||
|
|
|
|
|
(g2buf.hdr.flag[0] == 0x01) || /* allow the announcements from g2_link */
|
|
|
|
|
(g2buf.hdr.flag[0] == 0x08) ||
|
|
|
|
|
(g2buf.hdr.flag[0] == 0x20) ||
|
|
|
|
|
(g2buf.hdr.flag[0] == 0x28) ||
|
|
|
|
|
(g2buf.hdr.flag[0] == 0x40))) {
|
|
|
|
|
if (0==toRptr[i].last_time && 0==band_txt[i].last_time && (Flag_is_ok(g2buf.hdr.flag[0]) || 0x01U==g2buf.hdr.flag[0] || 0x40U==g2buf.hdr.flag[0])) {
|
|
|
|
|
if (bool_qso_details)
|
|
|
|
|
printf("id=%04x G2 start, ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s IP=%s:%u\n",
|
|
|
|
|
ntohs(g2buf.streamid), g2buf.hdr.urcall, g2buf.hdr.rpt1, g2buf.hdr.rpt2,
|
|
|
|
|
g2buf.hdr.mycall, g2buf.hdr.sfx, inet_ntoa(fromDst4.sin_addr), ntohs(fromDst4.sin_port));
|
|
|
|
|
printf("id=%04x G2 start, ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s IP=%s:%u\n", ntohs(g2buf.streamid), g2buf.hdr.urcall, g2buf.hdr.rpt1, g2buf.hdr.rpt2, g2buf.hdr.mycall, g2buf.hdr.sfx, inet_ntoa(fromDst4.sin_addr), ntohs(fromDst4.sin_port));
|
|
|
|
|
|
|
|
|
|
memcpy(rptrbuf.pkt_id, "DSTR", 4);
|
|
|
|
|
rptrbuf.counter = htons(is_icom ? G2_COUNTER_OUT++ : toRptr[i].G2_COUNTER++); // bump the counter
|
|
|
|
|
@ -1054,11 +949,37 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FD_CLR (g2_sock,&fdset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* process data coming from local repeater modules */
|
|
|
|
|
if (FD_ISSET(srv_sock, &fdset)) {
|
|
|
|
|
void CQnetGateway::ProcessRepeater()
|
|
|
|
|
{
|
|
|
|
|
// dtmf stuff
|
|
|
|
|
int dtmf_buf_count[3] = {0, 0, 0};
|
|
|
|
|
char dtmf_buf[3][MAX_DTMF_BUF + 1] = { {""}, {""}, {""} };
|
|
|
|
|
int dtmf_last_frame[3] = { 0, 0, 0 };
|
|
|
|
|
unsigned int dtmf_counter[3] = { 0, 0, 0 };
|
|
|
|
|
|
|
|
|
|
// text stuff
|
|
|
|
|
bool new_group[3] = { true, true, true };
|
|
|
|
|
int header_type = 0;
|
|
|
|
|
short to_print[3] = { 0, 0, 0 };
|
|
|
|
|
bool ABC_grp[3] = { false, false, false };
|
|
|
|
|
bool C_seen[3] = { false, false, false };
|
|
|
|
|
unsigned char tmp_txt[3];
|
|
|
|
|
|
|
|
|
|
char temp_radio_user[CALL_SIZE + 1];
|
|
|
|
|
char temp_mod;
|
|
|
|
|
|
|
|
|
|
char arearp_cs[CALL_SIZE + 1];
|
|
|
|
|
char zonerp_cs[CALL_SIZE + 1];
|
|
|
|
|
char ip[IP_SIZE + 1];
|
|
|
|
|
|
|
|
|
|
char tempfile[FILENAME_MAX + 1];
|
|
|
|
|
long num_recs = 0L;
|
|
|
|
|
short int rec_len = 56;
|
|
|
|
|
|
|
|
|
|
SDSVT g2buf;
|
|
|
|
|
|
|
|
|
|
socklen_t fromlen = sizeof(struct sockaddr_in);
|
|
|
|
|
int recvlen = recvfrom(srv_sock, rptrbuf.pkt_id, 58, 0, (struct sockaddr *)&fromRptr, &fromlen);
|
|
|
|
|
|
|
|
|
|
@ -1078,9 +999,7 @@ void CQnetGateway::process()
|
|
|
|
|
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 && rptrbuf.flag[2]==0x0 && rptrbuf.vpkt.icm_id==0x20 &&
|
|
|
|
|
(rptrbuf.remaining==0x30 || rptrbuf.remaining==0x13 || rptrbuf.remaining==0x16) ) {
|
|
|
|
|
} else if ( (recvlen==58 || recvlen==29 || recvlen==32) && rptrbuf.flag[0]==0x73 && rptrbuf.flag[1]==0x12 && rptrbuf.flag[2]==0x0 && rptrbuf.vpkt.icm_id==0x20 && (rptrbuf.remaining==0x30 || rptrbuf.remaining==0x13 || rptrbuf.remaining==0x16) ) {
|
|
|
|
|
if (is_icom) { // acknowledge packet to ICOM
|
|
|
|
|
SDSTR reply;
|
|
|
|
|
memcpy(reply.pkt_id, "DSTR", 4);
|
|
|
|
|
@ -1093,15 +1012,9 @@ void CQnetGateway::process()
|
|
|
|
|
if (recvlen == 58) {
|
|
|
|
|
|
|
|
|
|
if (bool_qso_details)
|
|
|
|
|
printf("id=%04x cntr=%04x start RPTR ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s ip=%s\n",
|
|
|
|
|
ntohs(rptrbuf.vpkt.streamid), ntohs(rptrbuf.counter), rptrbuf.vpkt.hdr.ur, rptrbuf.vpkt.hdr.r1,
|
|
|
|
|
rptrbuf.vpkt.hdr.r2, rptrbuf.vpkt.hdr.my, rptrbuf.vpkt.hdr.nm, inet_ntoa(fromRptr.sin_addr));
|
|
|
|
|
printf("id=%04x cntr=%04x start RPTR ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s ip=%s\n", ntohs(rptrbuf.vpkt.streamid), ntohs(rptrbuf.counter), rptrbuf.vpkt.hdr.ur, rptrbuf.vpkt.hdr.r1, rptrbuf.vpkt.hdr.r2, rptrbuf.vpkt.hdr.my, rptrbuf.vpkt.hdr.nm, inet_ntoa(fromRptr.sin_addr));
|
|
|
|
|
|
|
|
|
|
if (0==memcmp(rptrbuf.vpkt.hdr.r1, OWNER.c_str(), 7) && // rpt1 is this repeater
|
|
|
|
|
(rptrbuf.vpkt.hdr.flag[0]==0x00 || // normal
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x08 || // EMR
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x20 || // BREAK
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x28)) { // EMR + BREAK (0x1, announcements are not allowed)
|
|
|
|
|
if (0==memcmp(rptrbuf.vpkt.hdr.r1, OWNER.c_str(), 7) && Flag_is_ok(rptrbuf.vpkt.hdr.flag[0])) {
|
|
|
|
|
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
@ -1189,17 +1102,15 @@ void CQnetGateway::process()
|
|
|
|
|
memcmp(rptrbuf.vpkt.hdr.ur, "REF", 3) &&
|
|
|
|
|
memcmp(rptrbuf.vpkt.hdr.ur, "DCS", 3) &&
|
|
|
|
|
rptrbuf.vpkt.hdr.ur[0]!=' ' && // must have something
|
|
|
|
|
memcmp(rptrbuf.vpkt.hdr.ur, "CQCQCQ", 6)) { // urcall is NOT CQCQCQ
|
|
|
|
|
memcmp(rptrbuf.vpkt.hdr.ur, "CQCQCQ", 6) ) // urcall is NOT CQCQCQ
|
|
|
|
|
{
|
|
|
|
|
if ( rptrbuf.vpkt.hdr.ur[0]=='/' && // repeater routing!
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r1, OWNER.c_str(), 7) && // rpt1 this repeater
|
|
|
|
|
(rptrbuf.vpkt.hdr.r1[7]>='A' && rptrbuf.vpkt.hdr.r1[7]<='C') && // with a valid module
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r2, OWNER.c_str(), 7) && // rpt2 is this repeater
|
|
|
|
|
rptrbuf.vpkt.hdr.r2[7]=='G' && // local Gateway
|
|
|
|
|
(rptrbuf.vpkt.hdr.flag[0]== 0x00 || // normal
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]== 0x08 || // EMR
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]== 0x20 || // BK
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]== 0x28)) {// EMR + BK
|
|
|
|
|
|
|
|
|
|
Flag_is_ok(rptrbuf.vpkt.hdr.flag[0]) )
|
|
|
|
|
{
|
|
|
|
|
if (memcmp(rptrbuf.vpkt.hdr.ur+1, OWNER.c_str(), 6)) { // the value after the slash is NOT this repeater
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
@ -1273,16 +1184,13 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (memcmp(rptrbuf.vpkt.hdr.ur, OWNER.c_str(), 7) && // urcall is not this repeater
|
|
|
|
|
}
|
|
|
|
|
else if (memcmp(rptrbuf.vpkt.hdr.ur, OWNER.c_str(), 7) && // urcall is not this repeater
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r1, OWNER.c_str(), 7) && // rpt1 is this repeater
|
|
|
|
|
(rptrbuf.vpkt.hdr.r1[7]>='A'&& rptrbuf.vpkt.hdr.r1[7]<='C') && // mod is A,B,C
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r2, OWNER.c_str(), 7) && // rpt2 is this repeater
|
|
|
|
|
rptrbuf.vpkt.hdr.r2[7]=='G' && // local Gateway
|
|
|
|
|
|
|
|
|
|
(rptrbuf.vpkt.hdr.flag[0]==0x00 || // normal
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x08 || // EMR
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x20 || // BK
|
|
|
|
|
rptrbuf.vpkt.hdr.flag[0]==0x28)) { // EMR + BK
|
|
|
|
|
Flag_is_ok(rptrbuf.vpkt.hdr.flag[0])) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memset(temp_radio_user, ' ', 8);
|
|
|
|
|
@ -1348,7 +1256,9 @@ void CQnetGateway::process()
|
|
|
|
|
time(&(to_remote_g2[i].last_time));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1396,13 +1306,15 @@ void CQnetGateway::process()
|
|
|
|
|
toRptr[i].sequence = rptrbuf.vpkt.ctrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
printf("icom rule: no routing from %.8s to %s%c\n", rptrbuf.vpkt.hdr.r1, arearp_cs, temp_mod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " C0", 8)) {
|
|
|
|
|
}
|
|
|
|
|
else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " C0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1414,7 +1326,8 @@ void CQnetGateway::process()
|
|
|
|
|
} else
|
|
|
|
|
printf("No voicemail to clear or still recording\n");
|
|
|
|
|
}
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " R0", 8)) {
|
|
|
|
|
}
|
|
|
|
|
else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " R0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1428,7 +1341,8 @@ void CQnetGateway::process()
|
|
|
|
|
} else
|
|
|
|
|
printf("No voicemail to recall or still recording\n");
|
|
|
|
|
}
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " S0", 8)) {
|
|
|
|
|
}
|
|
|
|
|
else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " S0", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1474,7 +1388,8 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " E", 8)) {
|
|
|
|
|
}
|
|
|
|
|
else if (0 == memcmp(rptrbuf.vpkt.hdr.ur, " E", 8)) {
|
|
|
|
|
int i = rptrbuf.vpkt.hdr.r1[7] - 'A';
|
|
|
|
|
|
|
|
|
|
if (i>=0 && i<3) {
|
|
|
|
|
@ -1521,7 +1436,8 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* check for cross-banding */
|
|
|
|
|
} else if ( 0==memcmp(rptrbuf.vpkt.hdr.ur, "CQCQCQ", 6) && // yrcall is CQCQCQ
|
|
|
|
|
}
|
|
|
|
|
else if ( 0==memcmp(rptrbuf.vpkt.hdr.ur, "CQCQCQ", 6) && // yrcall is CQCQCQ
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r2, OWNER.c_str(), 7) && // rpt1 is this repeater
|
|
|
|
|
0==memcmp(rptrbuf.vpkt.hdr.r1, OWNER.c_str(), 7) && // rpt2 is this repeater
|
|
|
|
|
(rptrbuf.vpkt.hdr.r1[7]>='A' && rptrbuf.vpkt.hdr.r1[7]<='C') && // mod of rpt1 is A,B,C
|
|
|
|
|
@ -1567,7 +1483,9 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // recvlen is 29 or 32
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
@ -1596,9 +1514,7 @@ void CQnetGateway::process()
|
|
|
|
|
dtmf_last_frame[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ii->sendHeardWithTXStats(band_txt[i].lh_mycall, band_txt[i].lh_sfx, band_txt[i].lh_yrcall, band_txt[i].lh_rpt1,
|
|
|
|
|
band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].num_dv_frames,
|
|
|
|
|
band_txt[i].num_dv_silent_frames, band_txt[i].num_bit_errors);
|
|
|
|
|
ii->sendHeardWithTXStats(band_txt[i].lh_mycall, band_txt[i].lh_sfx, band_txt[i].lh_yrcall, band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].num_dv_frames, band_txt[i].num_dv_silent_frames, band_txt[i].num_bit_errors);
|
|
|
|
|
|
|
|
|
|
band_txt[i].streamID = 0;
|
|
|
|
|
band_txt[i].flags[0] = band_txt[i].flags[1] = band_txt[i].flags[2] = 0;
|
|
|
|
|
@ -1619,7 +1535,9 @@ void CQnetGateway::process()
|
|
|
|
|
band_txt[i].num_dv_silent_frames = 0;
|
|
|
|
|
band_txt[i].num_bit_errors = 0;
|
|
|
|
|
|
|
|
|
|
} else { // not the end of the voice stream
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // not the end of the voice stream
|
|
|
|
|
int ber_data[3];
|
|
|
|
|
int ber_errs = dstar_dv_decode(rptrbuf.vpkt.vasd.voice, ber_data);
|
|
|
|
|
if (ber_data[0] == 0xf85)
|
|
|
|
|
@ -1670,7 +1588,8 @@ void CQnetGateway::process()
|
|
|
|
|
new_group[i] = false;
|
|
|
|
|
to_print[i] = 0;
|
|
|
|
|
ABC_grp[i] = false;
|
|
|
|
|
} else if (header_type == 0x30) { /* GPS or GPS id or APRS */
|
|
|
|
|
}
|
|
|
|
|
else if (header_type == 0x30) { /* GPS or GPS id or APRS */
|
|
|
|
|
new_group[i] = false;
|
|
|
|
|
to_print[i] = tmp_txt[0] & 0x0f;
|
|
|
|
|
ABC_grp[i] = false;
|
|
|
|
|
@ -1720,7 +1639,9 @@ void CQnetGateway::process()
|
|
|
|
|
band_txt[i].temp_line_cnt = 0;
|
|
|
|
|
}
|
|
|
|
|
to_print[i] -= 2;
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* something went wrong? all bets are off */
|
|
|
|
|
if (band_txt[i].temp_line_cnt > 200) {
|
|
|
|
|
printf("Reached the limit in the OLD gps mode\n");
|
|
|
|
|
@ -1752,7 +1673,8 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
to_print[i] --;
|
|
|
|
|
}
|
|
|
|
|
} else if (header_type == 0x40) { /* ABC text */
|
|
|
|
|
}
|
|
|
|
|
else if (header_type == 0x40) { /* ABC text */
|
|
|
|
|
new_group[i] = false;
|
|
|
|
|
to_print[i] = 3;
|
|
|
|
|
ABC_grp[i] = true;
|
|
|
|
|
@ -1790,7 +1712,9 @@ void CQnetGateway::process()
|
|
|
|
|
to_print[i] = 0;
|
|
|
|
|
ABC_grp[i] = false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (to_print[i] == 3) {
|
|
|
|
|
if (ABC_grp[i]) {
|
|
|
|
|
band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[0] ^ 0x70;
|
|
|
|
|
@ -1828,9 +1752,7 @@ void CQnetGateway::process()
|
|
|
|
|
// band_txt[i].dest_rptr[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, band_txt[i].lh_sfx,band_txt[i].lh_yrcall,
|
|
|
|
|
band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1],
|
|
|
|
|
band_txt[i].flags[2], band_txt[i].dest_rptr, band_txt[i].txt);
|
|
|
|
|
ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, band_txt[i].lh_sfx,band_txt[i].lh_yrcall, band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].dest_rptr, band_txt[i].txt);
|
|
|
|
|
band_txt[i].txt_stats_sent = true;
|
|
|
|
|
}
|
|
|
|
|
band_txt[i].txt_cnt = 0;
|
|
|
|
|
@ -1875,9 +1797,7 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
band_txt[i].temp_line[0] = '\0';
|
|
|
|
|
band_txt[i].temp_line_cnt = 0;
|
|
|
|
|
} else if (((tmp_txt[0] ^ 0x70) == '\n') ||
|
|
|
|
|
((tmp_txt[1] ^ 0x4f) == '\n') ||
|
|
|
|
|
((tmp_txt[2] ^ 0x93) == '\n')) {
|
|
|
|
|
} else if (((tmp_txt[0] ^ 0x70) == '\n') || ((tmp_txt[1] ^ 0x4f) == '\n') || ((tmp_txt[2] ^ 0x93) == '\n')) {
|
|
|
|
|
band_txt[i].temp_line[0] = '\0';
|
|
|
|
|
band_txt[i].temp_line_cnt = 0;
|
|
|
|
|
}
|
|
|
|
|
@ -1991,9 +1911,8 @@ void CQnetGateway::process()
|
|
|
|
|
to_remote_g2[i].last_time = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
/* Is the data to be recorded for echotest */
|
|
|
|
|
if (recd[i].fd>=0 && recd[i].streamid==rptrbuf.vpkt.streamid) {
|
|
|
|
|
}
|
|
|
|
|
else if (recd[i].fd>=0 && recd[i].streamid==rptrbuf.vpkt.streamid) { // Is the data to be recorded for echotest
|
|
|
|
|
time(&recd[i].last_time);
|
|
|
|
|
|
|
|
|
|
memcpy(recbuf.title, "DSVT", 4);
|
|
|
|
|
@ -2031,9 +1950,8 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
/* Is the data to be recorded for voicemail */
|
|
|
|
|
if ((vm[i].fd >= 0) && (vm[i].streamid==rptrbuf.vpkt.streamid)) {
|
|
|
|
|
}
|
|
|
|
|
else if ((vm[i].fd >= 0) && (vm[i].streamid==rptrbuf.vpkt.streamid)) { // Is the data to be recorded for voicemail
|
|
|
|
|
time(&vm[i].last_time);
|
|
|
|
|
|
|
|
|
|
memcpy(recbuf.title, "DSVT", 4);
|
|
|
|
|
@ -2061,9 +1979,8 @@ void CQnetGateway::process()
|
|
|
|
|
// printf("Closed voicemail audio file:[%s]\n", vm[i].file);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
/* or maybe this is cross-banding data */
|
|
|
|
|
if ((toRptr[i].streamid==rptrbuf.vpkt.streamid) && (toRptr[i].adr == fromRptr.sin_addr.s_addr)) {
|
|
|
|
|
}
|
|
|
|
|
else if ((toRptr[i].streamid==rptrbuf.vpkt.streamid) && (toRptr[i].adr == fromRptr.sin_addr.s_addr)) { // or maybe this is cross-banding data
|
|
|
|
|
sendto(srv_sock, rptrbuf.pkt_id, 29, 0, (struct sockaddr *)&toRptr[i].band_addr, sizeof(struct sockaddr_in));
|
|
|
|
|
|
|
|
|
|
/* timeit */
|
|
|
|
|
@ -2092,6 +2009,93 @@ void CQnetGateway::process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* run the main loop for QnetGateway */
|
|
|
|
|
void CQnetGateway::Process()
|
|
|
|
|
{
|
|
|
|
|
dstar_dv_init();
|
|
|
|
|
|
|
|
|
|
int max_nfds = 0;
|
|
|
|
|
if (g2_sock > max_nfds)
|
|
|
|
|
max_nfds = g2_sock;
|
|
|
|
|
if (srv_sock > max_nfds)
|
|
|
|
|
max_nfds = srv_sock;
|
|
|
|
|
printf("g2=%d, srv=%d, MAX+1=%d\n", g2_sock, srv_sock, max_nfds + 1);
|
|
|
|
|
|
|
|
|
|
std::future<void> aprs_future, irc_data_future;
|
|
|
|
|
if (bool_send_aprs) { // start the beacon thread
|
|
|
|
|
try {
|
|
|
|
|
aprs_future = std::async(std::launch::async, &CQnetGateway::APRSBeaconThread, this);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
printf("Failed to start the APRSBeaconThread. Exception: %s\n", e.what());
|
|
|
|
|
}
|
|
|
|
|
if (aprs_future.valid())
|
|
|
|
|
printf("APRS beacon thread started\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try { // start the IRC read thread
|
|
|
|
|
irc_data_future = std::async(std::launch::async, &CQnetGateway::GetIRCDataThread, this);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
printf("Failed to start GetIRCDataThread. Exception: %s\n", e.what());
|
|
|
|
|
keep_running = false;
|
|
|
|
|
}
|
|
|
|
|
if (keep_running)
|
|
|
|
|
printf("get_irc_data thread started\n");
|
|
|
|
|
|
|
|
|
|
ii->kickWatchdog(IRCDDB_VERSION);
|
|
|
|
|
|
|
|
|
|
if (is_icom) {
|
|
|
|
|
// send INIT to Icom Stack
|
|
|
|
|
unsigned char buf[500];
|
|
|
|
|
memset(buf, 0, 10);
|
|
|
|
|
memcpy(buf, "INIT", 4);
|
|
|
|
|
buf[6] = 0x73U;
|
|
|
|
|
// 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");
|
|
|
|
|
} else
|
|
|
|
|
printf("Skipping ICOM initialization\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (keep_running) {
|
|
|
|
|
ProcessTimeouts();
|
|
|
|
|
|
|
|
|
|
// wait 20 ms max
|
|
|
|
|
fd_set fdset;
|
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
|
FD_SET(g2_sock, &fdset);
|
|
|
|
|
FD_SET(srv_sock, &fdset);
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
|
tv.tv_usec = 20000; // 20 ms
|
|
|
|
|
(void)select(max_nfds + 1, &fdset, 0, 0, &tv);
|
|
|
|
|
|
|
|
|
|
// process packets coming from remote G2
|
|
|
|
|
if (FD_ISSET(g2_sock, &fdset)) {
|
|
|
|
|
ProcessRouting();
|
|
|
|
|
FD_CLR (g2_sock,&fdset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// process packets coming from local repeater modules
|
|
|
|
|
if (FD_ISSET(srv_sock, &fdset)) {
|
|
|
|
|
ProcessRepeater();
|
|
|
|
|
FD_CLR (srv_sock,&fdset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -2461,7 +2465,7 @@ void CQnetGateway::qrgs_and_maps()
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CQnetGateway::init(char *cfgfile)
|
|
|
|
|
int CQnetGateway::Init(char *cfgfile)
|
|
|
|
|
{
|
|
|
|
|
short int i;
|
|
|
|
|
struct sigaction act;
|
|
|
|
|
@ -2932,8 +2936,8 @@ int main(int argc, char **argv)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
CQnetGateway QnetGateway;
|
|
|
|
|
if (QnetGateway.init(argv[1]))
|
|
|
|
|
if (QnetGateway.Init(argv[1]))
|
|
|
|
|
return 1;
|
|
|
|
|
QnetGateway.process();
|
|
|
|
|
QnetGateway.Process();
|
|
|
|
|
printf("Leaving processing loop...\n");
|
|
|
|
|
}
|
|
|
|
|
|