/* * Copyright (C) 2010, 2011 by Scott Lawson KI4LKF * * Copyright (C) 2015 by Thomas A. Early N7TAE * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /*** KI4LKF, N7TAE ***/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "versions.h" #include #include #include #include #include #include #include using namespace libconfig; #include "DVAPDongle.h" #include "QnetTypeDefs.h" #define VERSION DVAP_VERSION #define CALL_SIZE 8 #define RPTR_SIZE 8 #define IP_SIZE 15 typedef struct dvap_ack_arg_tag { char mycall[8]; float ber; } SDVAP_ACK_ARG; /* Default configuration data */ static char RPTR[RPTR_SIZE + 1]; static char OWNER[RPTR_SIZE + 1]; static char RPTR_MOD; static char RPTR_VIRTUAL_IP[IP_SIZE + 1]; static int RPTR_PORT; static char G2_INTERNAL_IP[IP_SIZE + 1]; static int G2_PORT; static char DVP_SERIAL[64]; /* APxxxxxx */ static int DVP_FREQ; /* between 144000000 and 148000000 */ static int DVP_PWR; /* between -12 and 10 */ static int DVP_SQL; /* between -128 and -45 */ static int DVP_OFF; /* between -2000 and 2000 */ static int WAIT_FOR_PACKETS; /* wait 25 ms in reading from local G2 */ static int REMOTE_TIMEOUT; /* 1 second */ static int DELAY_BETWEEN; static int DELAY_BEFORE; static bool RPTR_ACK; static char INVALID_YRCALL_KEY[CALL_SIZE + 1]; static int inactiveMax = 25; /* helper data */ static unsigned char SND_TERM_ID; static char RPTR_and_G[9]; static char RPTR_and_MOD[9]; static int insock = -1; static struct sockaddr_in outaddr; static int serfd = -1; static bool busy20000 = false; std::atomic keep_running(true); static unsigned int space = 0; static unsigned int aseed = 0; /* helper routines */ static int read_config(const char *cfgFile); static void sig_catch(int signum); static int open_sock(); static void readFrom20000(); static void calcPFCS(unsigned char *packet, unsigned char *pfcs); static void ReadDVAPThread(); static void RptrAckThread(SDVAP_ACK_ARG *parg); /*** BER stuff ***/ extern void dstar_dv_init(); extern int dstar_dv_decode(const unsigned char *d, int data[3]); CDVAPDongle dongle; static void calcPFCS(unsigned char *packet, unsigned char *pfcs) { unsigned short crc_dstar_ffff = 0xffff; unsigned short tmp, short_c; unsigned short crc_tabccitt[256] = { 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, 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, 0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974, 0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3, 0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72, 0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1, 0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70, 0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff, 0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e, 0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd, 0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c, 0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb, 0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a, 0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9, 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 }; for (int i = 0; i < 39 ; i++) { short_c = 0x00ff & (unsigned short)packet[i]; tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; } crc_dstar_ffff = ~crc_dstar_ffff; tmp = crc_dstar_ffff; pfcs[0] = (unsigned char)(crc_dstar_ffff & 0xff); pfcs[1] = (unsigned char)((tmp >> 8) & 0xff); return; } static void sig_catch(int signum) { if ((signum == SIGTERM) || (signum == SIGINT) || (signum == SIGHUP)) keep_running = false; exit(0); } bool get_value(const Config &cfg, const char *path, int &value, int min, int max, int default_value) { if (cfg.lookupValue(path, value)) { if (value < min || value > max) value = default_value; } else value = default_value; printf("%s = [%d]\n", path, value); return true; } bool get_value(const Config &cfg, const char *path, double &value, double min, double max, double default_value) { if (cfg.lookupValue(path, value)) { if (value < min || value > max) value = default_value; } else value = default_value; printf("%s = [%lg]\n", path, value); return true; } bool get_value(const Config &cfg, const char *path, bool &value, bool default_value) { if (! cfg.lookupValue(path, value)) value = default_value; printf("%s = [%s]\n", path, value ? "true" : "false"); return true; } bool get_value(const Config &cfg, const char *path, std::string &value, int min, int max, const char *default_value) { if (cfg.lookupValue(path, value)) { int l = value.length(); if (lmax) { printf("%s is invalid\n", path); return false; } } else value = default_value; printf("%s = [%s]\n", path, value.c_str()); return true; } /* process configuration file */ static int read_config(const char *cfgFile) { int i; Config cfg; printf("Reading file %s\n", cfgFile); // Read the file. If there is an error, report it and exit. try { cfg.readFile(cfgFile); } catch(const FileIOException &fioex) { printf("Can't read %s\n", cfgFile); return 1; } catch(const ParseException &pex) { printf("Parse error at %s:%d - %s\n", pex.getFile(), pex.getLine(), pex.getError()); return 1; } std::string dvap_path, value; for (i=0; i<3; i++) { dvap_path = "module."; dvap_path += ('a' + i); if (cfg.lookupValue(dvap_path + ".type", value)) { if (0 == strcasecmp(value.c_str(), "dvap")) break; } } if (i >= 3) { printf("dvap not defined in any module!\n"); return 1; } RPTR_MOD = 'A' + i; if (cfg.lookupValue(std::string(dvap_path+".callsign").c_str(), value) || cfg.lookupValue("ircddb.login", value)) { int l = value.length(); if (l<3 || l>CALL_SIZE-2) { printf("Call '%s' is invalid length!\n", value.c_str()); return 1; } else { for (i=0; iCALL_SIZE-2) { printf("Call '%s' is invalid length!\n", value.c_str()); return 1; } else { for (i=0; i readthread; try { readthread = std::async(std::launch::async, ReadDVAPThread); } catch (const std::exception &e) { printf("Unable to start ReadDVAPThread(). Exception: %s\n", e.what()); keep_running = false; } printf("Started ReadDVAPThread()\n"); while (keep_running) { time(&tnow); if ((tnow - ackpoint) > 2) { rc = dongle.KeepAlive(); if (rc < 0) { cnt ++; if (cnt > 5) { printf("Could not send KEEPALIVE signal to dvap 5 times...exiting\n"); keep_running = false; } } else cnt = 0; ackpoint = tnow; } readFrom20000(); } readthread.get(); close(insock); printf("dvap_rptr exiting\n"); return 0; } static void RptrAckThread(SDVAP_ACK_ARG *parg) { char mycall[8]; memcpy(mycall, parg->mycall, 8); float ber = parg->ber; char RADIO_ID[21]; sprintf(RADIO_ID, "%20.2f", ber); memcpy(RADIO_ID, "BER%", 4); struct sigaction act; time_t tnow = 0; unsigned char silence[12] = { 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8,0x70,0x4f,0x93 }; unsigned int aseed_ack = 0; act.sa_handler = sig_catch; sigemptyset(&act.sa_mask); if (sigaction(SIGTERM, &act, 0) != 0) { printf("sigaction-TERM failed, error=%d\n", errno); return; } if (sigaction(SIGHUP, &act, 0) != 0) { printf("sigaction-HUP failed, error=%d\n", errno); return; } if (sigaction(SIGINT, &act, 0) != 0) { printf("sigaction-INT failed, error=%d\n", errno); return; } sleep(DELAY_BEFORE); time(&tnow); aseed_ack = tnow + getpid(); uint16_t stream_id_to_dvap = (rand_r(&aseed_ack) % 65535U) + 1U; // HEADER while ((space < 1) && keep_running) usleep(5); SDVAP_REGISTER dr; dr.header = 0xa02fu; dr.frame.streamid = stream_id_to_dvap; dr.frame.framepos = 0x80; dr.frame.seq = 0; dr.frame.hdr.flag[0] = 0x01; dr.frame.hdr.flag[1] = dr.frame.hdr.flag[2] = 0x00; memcpy(dr.frame.hdr.rpt1, RPTR_and_MOD, 8); memcpy(dr.frame.hdr.rpt2, RPTR_and_G, 8); memcpy(dr.frame.hdr.urcall, mycall, 8); memcpy(dr.frame.hdr.mycall, RPTR_and_MOD, 8); memcpy(dr.frame.hdr.sfx, (unsigned char *)" ", 4); calcPFCS(dr.frame.hdr.flag, dr.frame.hdr.pfcs); dongle.SendRegister(dr); std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_BETWEEN)); // SYNC dr.header = 0xc012u; dr.frame.streamid = stream_id_to_dvap; for (int i=0; i<10; i++) { while ((space < 1) && keep_running) usleep(5); dr.frame.framepos = dr.frame.seq = i; switch (i) { case 0: silence[9] = 0x55; silence[10] = 0x2d; silence[11] = 0x16; break; case 1: silence[9] = '@' ^ 0x70; silence[10] = RADIO_ID[0] ^ 0x4f; silence[11] = RADIO_ID[1] ^ 0x93; break; case 2: silence[9] = RADIO_ID[2] ^ 0x70; silence[10] = RADIO_ID[3] ^ 0x4f; silence[11] = RADIO_ID[4] ^ 0x93; break; case 3: silence[9] = 'A' ^ 0x70; silence[10] = RADIO_ID[5] ^ 0x4f; silence[11] = RADIO_ID[6] ^ 0x93; break; case 4: silence[9] = RADIO_ID[7] ^ 0x70; silence[10] = RADIO_ID[8] ^ 0x4f; silence[11] = RADIO_ID[9] ^ 0x93; break; case 5: silence[9] = 'B' ^ 0x70; silence[10] = RADIO_ID[10] ^ 0x4f; silence[11] = RADIO_ID[11] ^ 0x93; break; case 6: silence[9] = RADIO_ID[12] ^ 0x70; silence[10] = RADIO_ID[13] ^ 0x4f; silence[11] = RADIO_ID[14] ^ 0x93; break; case 7: silence[9] = 'C' ^ 0x70; silence[10] = RADIO_ID[15] ^ 0x4f; silence[11] = RADIO_ID[16] ^ 0x93; break; case 8: silence[9] = RADIO_ID[17] ^ 0x70; silence[10] = RADIO_ID[18] ^ 0x4f; silence[11] = RADIO_ID[19] ^ 0x93; break; case 9: silence[0] = 0x55; silence[1] = 0xc8; silence[2] = 0x7a; silence[9] = 0x55; silence[10] = 0x55; silence[11] = 0x55; dr.frame.framepos |= 0x40; break; } memcpy(&dr.frame.vad.voice, silence, 12); dongle.SendRegister(dr); if (i < 9) std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_BETWEEN)); } return; } static void ReadDVAPThread() { REPLY_TYPE reply; SPKT net_buf; SPKT spack; SDVAP_REGISTER dr; time_t tnow = 0; time_t S_ctrl_msg_time = 0; unsigned short C_COUNTER = 0; time_t last_RF_time = 0; struct sigaction act; bool dvap_busy = false; // bool ptt = false; bool the_end = true; bool ok = true; int i = 0; u_int16_t streamid_raw = 0; short int sequence = 0; char mycall[8]; short int status_cntr = 3000; char temp_yrcall[CALL_SIZE + 1]; char *temp_ptr = NULL; int num_dv_frames = 0; int num_bit_errors = 0; act.sa_handler = sig_catch; sigemptyset(&act.sa_mask); if (sigaction(SIGTERM, &act, 0) != 0) { printf("sigaction-TERM failed, error=%d\n", errno); keep_running = false; return; } if (sigaction(SIGHUP, &act, 0) != 0) { printf("sigaction-HUP failed, error=%d\n", errno); keep_running = false; return; } if (sigaction(SIGINT, &act, 0) != 0) { printf("sigaction-INT failed, error=%d\n", errno); keep_running = false; return; } /* prepare the S server status packet */ memcpy(spack.pkt_id, "DSTR", 4); spack.counter = 0; spack.flag[0] = 0x73; spack.flag[1] = 0x21; spack.flag[2] = 0x00; spack.remaining = 0x10; while (keep_running) { time(&tnow); /* send the S packet if needed */ if ((tnow - S_ctrl_msg_time) > 60) { spack.counter = C_COUNTER++; memcpy(spack.spkt.mycall, OWNER, 7); spack.spkt.mycall[7] = 'S'; memcpy(spack.spkt.rpt, OWNER, 7); spack.spkt.rpt[7] = 'S'; sendto(insock, spack.pkt_id, 26, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); S_ctrl_msg_time = tnow; } // local RF user went away ? if (dvap_busy) { time(&tnow); if ((tnow - last_RF_time) > 1) dvap_busy = false; } // read from the dvap and process reply = dongle.GetReply(dr); if (reply == RT_ERR) { printf("Detected ERROR event from DVAP dongle, stopping...n"); break; } else if (reply == RT_STOP) { printf("Detected STOP event from DVAP dongle, stopping...\n"); break; } else if (reply == RT_START) { printf("Detected START event from DVAP dongle\n"); // else if (reply == RT_PTT) { // ptt = (dvp_buf[4] == 0x01); // printf("Detected PTT=%s\n", ptt?"on":"off"); } else if (reply == RT_STS) { space = (unsigned int)dr.param.sstr[2]; if (status_cntr < 3000) status_cntr += 20; } else if (reply == RT_HDR) { num_dv_frames = 0; num_bit_errors = 0; printf("From DVAP: flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s\n", dr.frame.hdr.flag[0], dr.frame.hdr.flag[1], dr.frame.hdr.flag[2], dr.frame.hdr.mycall, dr.frame.hdr.sfx, dr.frame.hdr.urcall, dr.frame.hdr.rpt2, dr.frame.hdr.rpt1); ok = true; /* Accept valid flags only */ if (ok) { 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] != 0x40) && (dr.frame.hdr.flag[0] != 0x48) && // rptr (dr.frame.hdr.flag[0] != 0x60) && (dr.frame.hdr.flag[0] != 0x68)) // flags ok = false; } /* Reject those stupid STN stations */ if (ok) { memcpy(temp_yrcall, dr.frame.hdr.urcall, CALL_SIZE); temp_yrcall[CALL_SIZE] = '\0'; temp_ptr = strstr(temp_yrcall, INVALID_YRCALL_KEY); if (temp_ptr == temp_yrcall) { // found it at first position printf("YRCALL value [%s] starts with the INVALID_YRCALL_KEY [%s], resetting to CQCQCQ\n", temp_yrcall, INVALID_YRCALL_KEY); memcpy(dr.frame.hdr.urcall, "CQCQCQ ", 8); } } memcpy(&net_buf.vpkt.hdr, dr.frame.hdr.flag, 41); // copy the header /* RPT1 must always be the repeater + module */ memcpy(net_buf.vpkt.hdr.rpt2, RPTR_and_MOD, 8); /* copy RPT2 */ memcpy(net_buf.vpkt.hdr.rpt1, dr.frame.hdr.rpt1, 8); /* RPT2 must also be valid */ if ((net_buf.vpkt.hdr.rpt1[7] == 'A') || (net_buf.vpkt.hdr.rpt1[7] == 'B') || (net_buf.vpkt.hdr.rpt1[7] == 'C') || (net_buf.vpkt.hdr.rpt1[7] == 'G')) memcpy(net_buf.vpkt.hdr.rpt1, RPTR, 7); else memset(net_buf.vpkt.hdr.rpt1, ' ', 8); if ((memcmp(net_buf.vpkt.hdr.urcall, "CQCQCQ", 6) != 0) && (net_buf.vpkt.hdr.rpt1[0] != ' ')) memcpy(net_buf.vpkt.hdr.rpt1, RPTR_and_G, 8); /* 8th in rpt1, rpt2 must be diff */ if (net_buf.vpkt.hdr.rpt1[7] == net_buf.vpkt.hdr.rpt2[7]) memset(net_buf.vpkt.hdr.rpt1, ' ', 8); /* Are we restricting the RF user ? If RPTR is OWNER, then any RF user can talk. If RPTR is not OWNER, that means that mycall, rpt1, rpt2 must be equal to RPTR otherwise we drop the rf data */ if (memcmp(RPTR, OWNER, RPTR_SIZE) != 0) { if (memcmp(net_buf.vpkt.hdr.mycall, RPTR, RPTR_SIZE) != 0) { printf("mycall=[%.8s], not equal to %s\n", net_buf.vpkt.hdr.mycall, RPTR); ok = false; } } else if (memcmp(net_buf.vpkt.hdr.mycall, " ", 8) == 0) { printf("Invalid value for mycall=[%.8s]\n", net_buf.vpkt.hdr.mycall); ok = false; } if (ok) { for (i = 0; i < 8; i++) { if (!isupper(net_buf.vpkt.hdr.mycall[i]) && !isdigit(net_buf.vpkt.hdr.mycall[i]) && (net_buf.vpkt.hdr.mycall[i] != ' ')) { memset(net_buf.vpkt.hdr.mycall, ' ', 8); ok = false; printf("Invalid value for MYCALL\n"); break; } } for (i = 0; i < 4; i++) { if (!isupper(net_buf.vpkt.hdr.sfx[i]) && !isdigit(net_buf.vpkt.hdr.sfx[i]) && (net_buf.vpkt.hdr.sfx[i] != ' ')) { memset(net_buf.vpkt.hdr.sfx, ' ', 4); break; } } for (i = 0; i < 8; i++) { if (!isupper(net_buf.vpkt.hdr.urcall[i]) && !isdigit(net_buf.vpkt.hdr.urcall[i]) && (net_buf.vpkt.hdr.urcall[i] != ' ') && (net_buf.vpkt.hdr.urcall[i] != '/')) { memcpy(net_buf.vpkt.hdr.urcall, "CQCQCQ ", 8); break; } } /*** what if YRCALL is all spaces, we can NOT allow that ***/ if (memcmp(net_buf.vpkt.hdr.urcall, " ", 8) == 0) memcpy(net_buf.vpkt.hdr.urcall, "CQCQCQ ", 8); /* change the rptr flags to net flags */ if (dr.frame.hdr.flag[0] == 0x40) net_buf.vpkt.hdr.flag[0] = 0x00; else if (dr.frame.hdr.flag[0] == 0x48) net_buf.vpkt.hdr.flag[0] = 0x08; else if (dr.frame.hdr.flag[0] == 0x60) net_buf.vpkt.hdr.flag[0] = 0x20; else if (dr.frame.hdr.flag[0] == 0x68) net_buf.vpkt.hdr.flag[0] = 0x28; else net_buf.vpkt.hdr.flag[0] = 0x00; net_buf.vpkt.hdr.flag[1] = net_buf.vpkt.hdr.flag[2] = 0x00; /* for icom g2 */ spack.counter = C_COUNTER++; memcpy(spack.spkt.mycall, net_buf.vpkt.hdr.mycall, 8); memcpy(spack.spkt.rpt, OWNER, 7); spack.spkt.rpt[7] = RPTR_MOD; sendto(insock, spack.pkt_id, 26, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); // Before we send the data to the local gateway, // set RPT1, RPT2 to be the local gateway memcpy(net_buf.vpkt.hdr.rpt2, OWNER, 7); if (net_buf.vpkt.hdr.rpt1[7] != ' ') memcpy(net_buf.vpkt.hdr.rpt1, OWNER, 7); memcpy(net_buf.pkt_id, "DSTR", 4); net_buf.counter = C_COUNTER++; net_buf.flag[0] = 0x73; net_buf.flag[1] = 0x12; net_buf.flag[2] = 0x00; net_buf.remaining = 0x30; net_buf.vpkt.icm_id = 0x20; net_buf.vpkt.dst_rptr_id = 0x00; net_buf.vpkt.snd_rptr_id = 0x01; net_buf.vpkt.snd_term_id = SND_TERM_ID; streamid_raw = (rand_r(&aseed) % 65535U) + 1U; net_buf.vpkt.streamid = streamid_raw; net_buf.vpkt.ctrl = 0x80; sequence = 0; calcPFCS((unsigned char *)&(net_buf.vpkt.hdr), net_buf.vpkt.hdr.pfcs); sendto(insock, &net_buf, 58, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); // local RF user keying up, start timer dvap_busy = true; time(&last_RF_time); // save mycall for the ack later memcpy(mycall, dr.frame.hdr.mycall, 8); } } else if (reply == RT_DAT) { /* have we already received a header ? */ if (dvap_busy) { the_end = ((dr.frame.framepos & 0x40) == 0x40); net_buf.counter = C_COUNTER++; net_buf.remaining = 0x13; net_buf.vpkt.ctrl = sequence++; if (the_end) net_buf.vpkt.ctrl = sequence | 0x40; memcpy(&net_buf.vpkt.vasd, &dr.frame.vad.voice, 12); sendto(insock, &net_buf, 29, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); int ber_data[3]; int ber_errs = dstar_dv_decode(net_buf.vpkt.vasd.voice, ber_data); if (ber_data[0] != 0xf85) { num_bit_errors += ber_errs; num_dv_frames++; } if (sequence > 0x14) sequence = 0; // local RF user still talking, update timer time(&last_RF_time); if (the_end) { // local RF user stopped talking dvap_busy = false; static SDVAP_ACK_ARG dvap_ack_arg; dvap_ack_arg.ber = (num_dv_frames==0) ? 0.f : 100.f * (float)num_bit_errors / (float)(num_dv_frames * 24); printf("End of dvap audio, ber=%.02f\n", dvap_ack_arg.ber); if (RPTR_ACK && !busy20000) { memcpy(dvap_ack_arg.mycall, mycall, 8); try { std::async(std::launch::async, RptrAckThread, &dvap_ack_arg); } catch (const std::exception &e) { printf("Failed to start RptrAckThread(). Exception: %s\n", e.what()); } } } } } usleep(1000); status_cntr--; if (status_cntr < 0) break; } /* stop dvap */ dongle.Stop(); close(serfd); printf("ReadDVAPThread exiting\n"); keep_running = false; return; }