process slow data from modem

pull/14/head
Tom Early 6 years ago
parent 76c0b48fc4
commit a72f121ffa

@ -97,6 +97,28 @@ bool CLocation::Parse(const char *instr)
return true;
}
const char *CLocation::APRS(std::string &call, const char *station)
{
char last;
call.resize(8, ' ');
last = call.at(7);
auto pos = call.find(' ');
if (call.npos != pos) {
call.resize(pos+1);
}
double latmin, lonmin;
double lat = modf(fabs(latitude), &latmin);
latmin *= 60.0;
double lon = modf(fabs(longitude), &lonmin);
lonmin *= 60.0;
if (last == ' ')
snprintf(aprs, 128, "%s>APDPRS,DSTAR*,qAR,%s:!%02d%04.2f%c/%03d%04.2f%c/A\r\n", call.c_str(), station, int(lat), latmin, (latitude>=0) ? 'N' : 'S', int(lon), lonmin, (longitude>=0) ? 'E' : 'W');
else
snprintf(aprs, 128, "%s-%c>APDPRS,DSTAR*,qAR,%s:!%02d%04.2f%c/%03d%04.2f%c/A\r\n", call.c_str(), last, station, int(lat), latmin, (latitude>=0) ? 'N' : 'S', int(lon), lonmin, (longitude>=0) ? 'E' : 'W');
return aprs;
}
double CLocation::Latitude() const
{
return latitude;

@ -18,6 +18,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string>
class CLocation
{
public:
@ -26,8 +28,10 @@ public:
double Latitude() const;
double Longitude() const;
const char* MaidenHead() const;
const char* APRS(std::string &call, const char *station);
private:
char maidenhead[7];
char aprs[128];
double latitude, longitude;
std::regex gps;
};

@ -54,7 +54,7 @@
#define CFG_DIR "/usr/local/etc"
#endif
const std::string GW_VERSION("QnetGateway-616");
const std::string GW_VERSION("QnetGateway-626");
int CQnetGateway::FindIndex(const int i) const
{
@ -600,24 +600,7 @@ void CQnetGateway::ProcessTimeouts()
if (to_remote_g2[i].toDstar.AddressIsZero())
printf("Inactivity from local rptr module %c, removing stream id %04x\n", 'A'+i, ntohs(band_txt[i].streamID));
band_txt[i].streamID = 0;
band_txt[i].flags[0] = band_txt[i].flags[1] = band_txt[i].flags[2] = 0x0;
band_txt[i].lh_mycall[0] = '\0';
band_txt[i].lh_sfx[0] = '\0';
band_txt[i].lh_yrcall[0] = '\0';
band_txt[i].lh_rpt1[0] = '\0';
band_txt[i].lh_rpt2[0] = '\0';
band_txt[i].last_time = 0;
band_txt[i].txt[0] = '\0';
band_txt[i].txt_cnt = 0;
band_txt[i].dest_rptr[0] = '\0';
band_txt[i].num_dv_frames = 0;
band_txt[i].num_dv_silent_frames = 0;
band_txt[i].num_bit_errors = 0;
band_txt[i].Initialize();
}
}
@ -682,301 +665,7 @@ bool CQnetGateway::ProcessG2Msg(const unsigned char *data, const int mod, std::s
return false;
}
// new_group is true if we are processing the first voice packet of a 2-voice packet pair. The high order nibble of the first byte of
// this first packet specifed the type of slow data that is being sent.
// the to_print is an integer that counts down how many 2-voice-frame pairs remain to be processed.
// ABC_grp means that we are processing a 20-character message.
// C_seen means that we are processing the last 2-voice-frame packet on a 20 character message.
void CQnetGateway::ProcessSlowData(unsigned char *data, const unsigned short sid)
{
/* extract 20-byte RADIO ID */
if (VoicePacketIsSync(data))
return;
// first, unscramble
const unsigned char c1 = data[0] ^ 0x70u;
const unsigned char c2 = data[1] ^ 0x4fu;
const unsigned char c3 = data[2] ^ 0x93u;
for (int i=0; i<3; i++) {
if (band_txt[i].streamID == sid) {
if (new_group[i]) {
const unsigned char header_type = c1 & 0xf0;
// header squelch
if ((header_type == 0x50) || (header_type == 0xc0)) {
new_group[i] = false;
to_print[i] = 0;
ABC_grp[i] = false;
}
else if (header_type == 0x30) { /* GPS or GPS id or APRS */
new_group[i] = false;
to_print[i] = c1 & 0x0f;
ABC_grp[i] = false;
if (to_print[i] > 5)
to_print[i] = 5;
else if (to_print[i] < 1)
to_print[i] = 1;
if ((to_print[i] > 1) && (to_print[i] <= 5)) {
/* 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");
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* fresh GPS string, re-initialize */
if ((to_print[i] == 5) && (c2 == '$')) {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* do not copy CR, NL */
if ((c2 != '\r') && (c2 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c2;
}
if ((c3 != '\r') && (c3 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c3;
}
if ((c2 == '\r') || (c3 == '\r')) {
if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) {
memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0';
} else if (band_txt[i].temp_line[0] != '$') {
memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0';
if (APRS_ENABLE && !band_txt[i].is_gps_sent)
gps_send(i);
}
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
} else if ((c2 == '\n') || (c3 == '\n')) {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
to_print[i] -= 2;
} 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");
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* do not copy CR, NL */
if ((c2 != '\r') && (c2 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c2;
}
if (c2 == '\r') {
if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) {
memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0';
} else if (band_txt[i].temp_line[0] != '$') {
memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0';
if (APRS_ENABLE && !band_txt[i].is_gps_sent)
gps_send(i);
}
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
} else if (c2 == '\n') {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
to_print[i] --;
}
}
else if (header_type == 0x40) { /* ABC text */
new_group[i] = false;
to_print[i] = 3;
ABC_grp[i] = true;
C_seen[i] = ((c1 & 0x0f) == 0x03) ? true : false;
band_txt[i].txt[band_txt[i].txt_cnt++] = c2;
band_txt[i].txt[band_txt[i].txt_cnt++] = c3;
/* We should NOT see any more text,
if we already processed text,
so blank out the codes. */
if (band_txt[i].sent_key_on_msg) {
data[0] = 0x70;
data[1] = 0x4f;
data[2] = 0x93;
}
if (band_txt[i].txt_cnt >= 20) {
band_txt[i].txt[band_txt[i].txt_cnt] = '\0';
band_txt[i].txt_cnt = 0;
}
}
else { // header type is not header, squelch, gps or message
new_group[i] = false;
to_print[i] = 0;
ABC_grp[i] = false;
}
}
else { // not a new_group, this is the second of a two-voice-frame pair
if (! band_txt[i].sent_key_on_msg && vPacketCount[i] > 100) {
// 100 voice packets received and still no 20-char message!
/*** if YRCALL is CQCQCQ, set dest_rptr ***/
band_txt[i].txt[0] = '\0';
if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0)
set_dest_rptr(i+'A', band_txt[i].dest_rptr);
int x = FindIndex(i);
if (x >= 0)
ii[x]->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], IS_HF[i] ? "" : band_txt[i].dest_rptr, band_txt[i].txt);
band_txt[i].sent_key_on_msg = true;
}
if (to_print[i] == 3) {
if (ABC_grp[i]) {
band_txt[i].txt[band_txt[i].txt_cnt++] = c1;
band_txt[i].txt[band_txt[i].txt_cnt++] = c2;
band_txt[i].txt[band_txt[i].txt_cnt++] = c3;
/* We should NOT see any more text,
if we already processed text,
so blank out the codes. */
if (band_txt[i].sent_key_on_msg) {
data[0] = 0x70;
data[1] = 0x4f;
data[2] = 0x93;
}
if ((band_txt[i].txt_cnt >= 20) || C_seen[i]) {
band_txt[i].txt[band_txt[i].txt_cnt] = '\0';
if ( ! band_txt[i].sent_key_on_msg) {
/*** if YRCALL is CQCQCQ, set dest_rptr ***/
if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0)
set_dest_rptr(i+'A', band_txt[i].dest_rptr);
// we have the 20-character message, send it to the server...
int x = FindIndex(i);
if (x >= 0)
ii[x]->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], IS_HF[i] ? "" : band_txt[i].dest_rptr, band_txt[i].txt);
band_txt[i].sent_key_on_msg = true;
}
band_txt[i].txt_cnt = 0;
}
if (C_seen[i])
C_seen[i] = false;
} 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");
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* do not copy carrige return or newline */
if ((c1 != '\r') && (c1 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c1;
}
if ((c2 != '\r') && (c2 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c2;
}
if ((c3 != '\r') && (c3 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c3;
}
if ( (c1 == '\r') || (c2 == '\r') || (c3 == '\r') ) {
if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) {
memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0';
} else if (band_txt[i].temp_line[0] != '$') {
memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0';
if (APRS_ENABLE && !band_txt[i].is_gps_sent)
gps_send(i);
}
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
else if ((c1 == '\n') || (c2 == '\n') ||(c3 == '\n')) {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
}
} else if (to_print[i] == 2) {
/* 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");
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* do not copy CR, NL */
if ((c1 != '\r') && (c1 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt] = c1;
band_txt[i].temp_line_cnt++;
}
if ((c2 != '\r') && (c2 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt] = c2;
band_txt[i].temp_line_cnt++;
}
if ((c1 == '\r') || (c2 == '\r')) {
if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) {
memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0';
} else if (band_txt[i].temp_line[0] != '$') {
memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0';
if (APRS_ENABLE && !band_txt[i].is_gps_sent)
gps_send(i);
}
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
} else if ((c1 == '\n') || (c2 == '\n')) {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
} else if (to_print[i] == 1) {
/* 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");
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
/* do not copy CR, NL */
if ((c1 != '\r') && (c1 != '\n')) {
band_txt[i].temp_line[band_txt[i].temp_line_cnt++] = c1;
}
if (c1 == '\r') {
if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) {
memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0';
} else if (band_txt[i].temp_line[0] != '$') {
memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt);
band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0';
if (APRS_ENABLE && !band_txt[i].is_gps_sent)
gps_send(i);
}
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
} else if (c1 == '\n') {
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
}
}
new_group[i] = true;
to_print[i] = 0;
ABC_grp[i] = false;
}
break;
}
}
}
void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt)
void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt, const int source_sock)
{
int i;
for (i=0; i<3; i++) {
@ -984,7 +673,7 @@ void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt)
break;
}
// if i==3, then the streamid of this voice packet didn't match any module
SSD &sd = Sd[i];
SSD &sd = sdin[i];
if (VoicePacketIsSync(dsvt.vasd.text)) {
sd.first = true;
@ -1054,20 +743,15 @@ void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt)
memcpy(sdheader.hdr.flag, sd.header, 39);
calcPFCS(sdheader.title, 56);
if (0 == memcmp(sd.header+39, sdheader.hdr.pfcs, 2)) { // checksum looks okay
printf("Got a slow data header: %36.36s\n", sd.header+3);
int mod = sdheader.hdr.rpt2[CALL_SIZE-1] - 'A'; // the sd header lists the gateway first, so we check here to see if there's a match
if (mod >= 0 && mod < 3 && Rptr.mod[mod].defined) {
printf("Got a slow data header: %36.36s\n", sd.header+3);
unsigned char call[CALL_SIZE]; // swap rpt1 and rpt2
memcpy(call, sdheader.hdr.rpt1, CALL_SIZE);
memcpy(sdheader.hdr.rpt1, sdheader.hdr.rpt2, CALL_SIZE);
memcpy(sdheader.hdr.rpt2, call, CALL_SIZE);
calcPFCS(sdheader.title, 56);
memcpy(toRptr[mod].saved_hdr.title, sdheader.title, 56); // copy to enable this and subsequent voice pacekets
ToModem[mod].Write(sdheader.title, 56); // send it to the mdeom
toRptr[mod].sequence = dsvt.ctrl;
time(&toRptr[mod].last_time);
if (LOG_QSO)
printf("Slow Data Header: id=0x%04x flags=%x:%x:%x r1=%8.8s r2=%8.8s ur=%8.8s my=%8.8s nm=%4.4s\n", htons(sdheader.streamid), sdheader.hdr.flag[0], sdheader.hdr.flag[1], sdheader.hdr.flag[2], sdheader.hdr.rpt1, sdheader.hdr.rpt2, sdheader.hdr.urcall, sdheader.hdr.mycall, sdheader.hdr.sfx);
ProcessG2Header(sdheader, source_sock); // start the voice stream
sd.ih = sd.size = 0;
} else {
fprintf(stderr, "Got a valid slow data header but module %d doesn't exist\n", mod);
@ -1081,8 +765,6 @@ void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt)
}
sd.first = false;
break;
default:
return;
}
} else {
// this is the second of a two voice-frame pair
@ -1139,14 +821,159 @@ void CQnetGateway::ProcessIncomingSD(const SDSVT &dsvt)
}
}
void CQnetGateway::ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int source_sock)
// source_sock is the socket number of the incoming data, or -1 if it's a unix socket
void CQnetGateway::ProcessOutGoingSD(const SDSVT &dsvt)
{
int i;
for (i=0; i<3; i++) {
if (Rptr.mod[i].defined && (toRptr[i].saved_hdr.streamid == dsvt.streamid))
break;
}
// if i==3, then the streamid of this voice packet didn't match any module
if (i > 2)
return;
SSD &sd = sdout[i];
if (VoicePacketIsSync(dsvt.vasd.text)) {
sd.first = true;
return;
}
const unsigned char c[3] = {
static_cast<unsigned char>(dsvt.vasd.text[0] ^ 0x70u),
static_cast<unsigned char>(dsvt.vasd.text[1] ^ 0x4fu),
static_cast<unsigned char>(dsvt.vasd.text[2] ^ 0x93u)
}; // unscramble
if (sd.first) {
// this is the first of a two voice-packet pair
// get the "size" and type from the first byte
sd.size = 0x0FU & c[0];
if (sd.size > 5) {
sd.size = 5;
}
int size = sd.size;
if (size > 2)
size = 2;
sd.type = 0xF0U & c[0];
switch (sd.type) {
case 0x30U: // GPS data
if (sd.size + sd.ig < 255) {
memcpy(sd.gps+sd.ig, c+1, size);
if (c[1]=='\r' || c[2]=='\r') {
sd.gps[sd.ig + ((c[1] == '\r') ? 0 : 1)] = '\0';
if (i < 3) {
Printable(sd.gps);
if (showLastHeard && gps.Parse((const char *)&sd.gps)) {
qnDB.UpdatePosition(band_txt[i].mycall.c_str(), gps.MaidenHead(), gps.Latitude(), gps.Longitude());
if (APRS_ENABLE && (! band_txt[i].is_gps_sent) && (time(NULL) - band_txt[i].gps_last_time > 30)) {
std::string call(band_txt[i].mycall);
const char *s = gps.APRS(call, Rptr.mod[i].call.c_str());
if (! aprs->aprs_sock.Write((unsigned char *)s, strlen(s))) {
time(&band_txt[i].gps_last_time);
band_txt[i].is_gps_sent = true;
}
}
}
}
sd.ig = sd.size = 0;
} else {
sd.ig += size;
sd.size -= size;
}
} else {
printf("GPS string is too large at %d bytes\n", sd.ig + sd.size);
sd.ig = sd.size = 0;
}
sd.first = false;
break;
case 0x40U: // 20 character user message
if (sd.size * 5 == sd.im) {
memcpy(sd.message+sd.im, c+1, 2);
sd.im += 2;
sd.size = 3;
} else {
//printf("A message voiceframe, #%d, is out of order because message size is %d\n", sd.size, sd.im);
sd.im = sd.size = 0;
}
sd.first = false;
break;
}
} else {
if (! band_txt[i].sent_key_on_msg && vPacketCount[i] > 100) {
// 100 voice packets received and still no 20-char message!
band_txt[i].txt.clear();
if (0 == band_txt[i].urcall.compare(0, 6, "CQCQCQ"))
set_dest_rptr(i+'A', band_txt[i].dest_rptr);
int x = FindIndex(i);
if (x >= 0)
ii[x]->sendHeardWithTXMsg(band_txt[i].mycall, band_txt[i].sfx, band_txt[i].urcall, band_txt[i].rpt1, band_txt[i].rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], IS_HF[i] ? "" : band_txt[i].dest_rptr, band_txt[i].txt);
band_txt[i].sent_key_on_msg = true;
}
// this is the second of a two voice-frame pair
sd.first = true;
if (0 == sd.size)
return;
switch (sd.type) {
case 0x30U: // GPS
memcpy(sd.gps+sd.ig, c, sd.size);
if (c[0]=='\r' || c[1]=='\r' || c[2]=='\r') {
if (c[0]=='\r')
sd.gps[sd.ig] = '\0';
else if (c[1]=='\r')
sd.gps[sd.ig+1] = '\0';
else
sd.gps[sd.ig+2] = '\0';
if (i < 3) {
Printable(sd.gps);
if (showLastHeard && gps.Parse((const char *)&sd.gps)) {
qnDB.UpdatePosition(band_txt[i].mycall.c_str(), gps.MaidenHead(), gps.Latitude(), gps.Longitude());
if (APRS_ENABLE && (! band_txt[i].is_gps_sent) && (time(NULL) - band_txt[i].gps_last_time > 30)) {
std::string call(band_txt[i].mycall);
const char *s = gps.APRS(call, Rptr.mod[i].call.c_str());
if (! aprs->aprs_sock.Write((unsigned char *)s, strlen(s))) {
time(&band_txt[i].gps_last_time);
band_txt[i].is_gps_sent = true;
}
}
}
}
sd.ig = 0;
} else {
sd.ig += sd.size;
sd.gps[sd.ig] = 0;
}
break;
case 0x40U: // message
memcpy(sd.message+sd.im, c, 3);
sd.im += 3;
if (sd.im >= 20) {
sd.message[20] = '\0';
Printable(sd.message);
if (! band_txt[i].sent_key_on_msg) {
if (0 == band_txt[i].urcall.compare(0, 6, "CQCQCQ"))
set_dest_rptr(i+'A', band_txt[i].dest_rptr);
int x = FindIndex(i);
if (x >= 0)
ii[x]->sendHeardWithTXMsg(band_txt[i].mycall, band_txt[i].sfx, band_txt[i].urcall, band_txt[i].rpt1, band_txt[i].rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], IS_HF[i] ? "" : band_txt[i].dest_rptr, (const char *)sd.message);
band_txt[i].sent_key_on_msg = true;
}
if (showLastHeard && (i < 3) && memcmp(toRptr[i].saved_hdr.hdr.sfx, "RPTR", 4) && memcmp(sd.message, "VIA SMARTGP", 11)) {
char call[CALL_SIZE+1];
memcpy(call, toRptr[i].saved_hdr.hdr.mycall, CALL_SIZE);
call[CALL_SIZE] = '\0';
qnDB.UpdateMessage(call, (const char *)&(sd.message));
}
sd.im = 0;
}
break;
}
}
}
void CQnetGateway::ProcessG2Header(const SDSVT &g2buf, const int source_sock)
{
static std::string lhcallsign[3], lhsfx[3];
static unsigned char nextctrl[3] = { 0U, 0U, 0U };
static std::string superframe[3];
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
int i = g2buf.hdr.rpt1[7] - 'A';
/* valid repeater module? */
@ -1155,8 +982,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int sou
// toRptr[i] is receiving data from a cross-band
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])) {
superframe[i].clear();
for (int z=0; z<4; z++)
Sd[z].Init(); // with a header, we should reset all Sd structs
sdin[i].Init(); // with a header, we should reset the Sd structs
if (LOG_QSO) {
printf("id=%04x flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s ", ntohs(g2buf.streamid), g2buf.hdr.flag[0], g2buf.hdr.flag[1], g2buf.hdr.flag[2], g2buf.hdr.urcall, g2buf.hdr.rpt1, g2buf.hdr.rpt2, g2buf.hdr.mycall, g2buf.hdr.sfx);
if (source_sock >= 0)
@ -1198,8 +1024,16 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int sou
toRptr[i].sequence = g2buf.ctrl;
}
}
}
void CQnetGateway::ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int source_sock)
// source_sock is the socket number of the incoming data, or -1 if it's a unix socket
{
if ( (g2buflen==56 || g2buflen==27) && 0==memcmp(g2buf.title, "DSVT", 4) && (g2buf.config==0x10 || g2buf.config==0x20) && g2buf.id==0x20) {
if (g2buflen == 56) {
ProcessG2Header(g2buf, source_sock);
} else { // g2buflen == 27
ProcessIncomingSD(g2buf);
ProcessIncomingSD(g2buf, source_sock);
/* find out which repeater module to send the data to */
int i;
for (i=0; i<3; i++) {
@ -1287,7 +1121,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int sou
printf("Final[%c]: %s\n", 'A'+i, superframe[i].c_str());
superframe[i].clear();
}
Sd[3].Init();
sdin[3].Init();
if (LOG_QSO)
printf("id=%04x END\n", ntohs(g2buf.streamid));
}
@ -1383,48 +1217,36 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
memcpy(band_txt[i].flags, dsvt.hdr.flag, 3);
memcpy(band_txt[i].lh_mycall, dsvt.hdr.mycall, 8);
band_txt[i].lh_mycall[8] = '\0';
memcpy(band_txt[i].lh_sfx, dsvt.hdr.sfx, 4);
band_txt[i].lh_sfx[4] = '\0';
memcpy(band_txt[i].lh_yrcall, dsvt.hdr.urcall, 8);
band_txt[i].lh_yrcall[8] = '\0';
memcpy(band_txt[i].lh_rpt1, dsvt.hdr.rpt1, 8);
band_txt[i].lh_rpt1[8] = '\0';
memcpy(band_txt[i].lh_rpt2, dsvt.hdr.rpt2, 8);
band_txt[i].lh_rpt2[8] = '\0';
band_txt[i].mycall.assign((const char *)dsvt.hdr.mycall, 8);
band_txt[i].sfx.assign((const char *)dsvt.hdr.sfx, 4);
band_txt[i].urcall.assign((const char *)dsvt.hdr.urcall, 8);
band_txt[i].rpt1.assign((const char *)dsvt.hdr.rpt1, 8);
band_txt[i].rpt2.assign((const char *)dsvt.hdr.rpt2, 8);
time(&band_txt[i].last_time);
band_txt[i].txt[0] = '\0';
band_txt[i].txt_cnt = 0;
band_txt[i].txt.clear();
band_txt[i].sent_key_on_msg = false;
band_txt[i].dest_rptr[0] = '\0';
/* try to process GPS mode: GPRMC and ID */
band_txt[i].temp_line[0] = '\0';
band_txt[i].temp_line_cnt = 0;
band_txt[i].gprmc[0] = '\0';
band_txt[i].gpid[0] = '\0';
band_txt[i].is_gps_sent = false;
// band_txt[i].gps_last_time = 0; DO NOT reset it
new_group[i] = true;
to_print[i] = 0;
ABC_grp[i] = false;
band_txt[i].num_dv_frames = 0;
band_txt[i].num_dv_silent_frames = 0;
band_txt[i].num_bit_errors = 0;
sdout[i].Init();
/* select the band for aprs processing, and lock on the stream ID */
if (APRS_ENABLE)
aprs->SelectBand(i, ntohs(dsvt.streamid));
if (std::regex_match(band_txt[i].mycall, preg)) {
qnDB.UpdateLH(band_txt[i].mycall.c_str(), band_txt[i].sfx.c_str(), 'A'+i, "Module ");
}
}
}
@ -1765,14 +1587,14 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
printf("Saving dtmfs=[%s] into file: [%s]\n", dtmf_buf[i], dtmf_file.c_str());
FILE *dtmf_fp = fopen(dtmf_file.c_str(), "w");
if (dtmf_fp) {
fprintf(dtmf_fp, "%s\n%s", dtmf_buf[i], band_txt[i].lh_mycall);
fprintf(dtmf_fp, "%s\n%s", dtmf_buf[i], band_txt[i].mycall.c_str());
fclose(dtmf_fp);
} else
printf("Failed to create dtmf file %s\n", dtmf_file.c_str());
if (LOG_DTMF)
printf("resetting dtmf[%d] (printed dtmf code %s from %s)\n", i, dtmf_buf[i], band_txt[i].lh_mycall);
printf("resetting dtmf[%d] (printed dtmf code %s from %s)\n", i, dtmf_buf[i], band_txt[i].mycall.c_str());
memset(dtmf_buf[i], 0, sizeof(dtmf_buf[i]));
dtmf_buf_count[i] = 0;
dtmf_counter[i] = 0;
@ -1780,12 +1602,12 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
}
if (! band_txt[i].sent_key_on_msg) {
band_txt[i].txt[0] = '\0';
if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0) {
if (0 == band_txt[i].urcall.compare(0, 6, "CQCQCQ")) {
set_dest_rptr(i+'A', band_txt[i].dest_rptr);
}
int x = FindIndex(i);
if (x >= 0)
ii[x]->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], IS_HF[i] ? "" : band_txt[i].dest_rptr, band_txt[i].txt);
ii[x]->sendHeardWithTXMsg(band_txt[i].mycall, band_txt[i].sfx, band_txt[i].urcall, band_txt[i].rpt1, band_txt[i].rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], IS_HF[i] ? "" : band_txt[i].dest_rptr, band_txt[i].txt);
band_txt[i].sent_key_on_msg = true;
}
// send the "key off" message, this will end up in the openquad.net Last Heard webpage.
@ -1798,36 +1620,19 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
}
}
if (index >= 0)
ii[index]->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[index]->sendHeardWithTXStats(band_txt[i].mycall, band_txt[i].sfx, band_txt[i].urcall, band_txt[i].rpt1, band_txt[i].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);
if (playNotInCache) {
// Not in cache, please try again!
FILE *fp = fopen(FILE_QNVOICE_FILE.c_str(), "w");
if (fp) {
fprintf(fp, "%c_notincache.dat_NOT_IN_CACHE\n", band_txt[i].lh_rpt1[7]);
fprintf(fp, "%c_notincache.dat_NOT_IN_CACHE\n", band_txt[i].rpt1.at(7));
fclose(fp);
}
playNotInCache = false;
}
band_txt[i].streamID = 0;
band_txt[i].flags[0] = band_txt[i].flags[1] = band_txt[i].flags[2] = 0;
band_txt[i].lh_mycall[0] = '\0';
band_txt[i].lh_sfx[0] = '\0';
band_txt[i].lh_yrcall[0] = '\0';
band_txt[i].lh_rpt1[0] = '\0';
band_txt[i].lh_rpt2[0] = '\0';
band_txt[i].last_time = 0;
band_txt[i].txt[0] = '\0';
band_txt[i].txt_cnt = 0;
band_txt[i].dest_rptr.clear();
band_txt[i].num_dv_frames = 0;
band_txt[i].num_dv_silent_frames = 0;
band_txt[i].num_bit_errors = 0;
band_txt[i].Initialize();
}
else
{ // not the end of the voice stream
@ -1863,7 +1668,8 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
}
vPacketCount[i]++;
}
ProcessSlowData(dsvt.vasd.text, dsvt.streamid);
ProcessOutGoingSD(dsvt);
//ProcessSlowData(dsvt.vasd.text, dsvt.streamid);
/* send data to qnlink */
ToLink.Write(dsvt.title, 27);
@ -1940,13 +1746,14 @@ void CQnetGateway::ProcessModem(const ssize_t recvlen, SDSVT &dsvt)
break;
}
}
if (LOG_QSO && dsvt.ctrl&0x40U)
if (0x40U & dsvt.ctrl) {
if (LOG_QSO)
printf("id=%04x END RPTR\n", ntohs(dsvt.streamid));
}
}
}
}
}
/* run the main loop for QnetGateway */
void CQnetGateway::Process()
@ -2427,27 +2234,7 @@ bool CQnetGateway::Init(char *cfgfile)
preg = std::regex("^(([1-9][A-Z])|([A-PR-Z][0-9])|([A-PR-Z][A-Z][0-9]))[0-9A-Z]*[A-Z][ ]*[ A-RT-Z]$", std::regex::extended);
for (i=0; i<3; i++) {
band_txt[i].streamID = 0;
memset(band_txt[i].flags, 0, 3);
memset(band_txt[i].lh_mycall, 0, 9);
memset(band_txt[i].lh_sfx, 0, 5);
memset(band_txt[i].lh_yrcall, 0, 9);
memset(band_txt[i].lh_rpt1, 0, 9);
memset(band_txt[i].lh_rpt2, 0, 9);
band_txt[i].last_time = 0;
memset(band_txt[i].txt, 0, 64); // Only 20 are used
band_txt[i].txt_cnt = 0;
band_txt[i].sent_key_on_msg = false;
band_txt[i].dest_rptr.clear();
memset(band_txt[i].temp_line, 0, 256);
band_txt[i].temp_line_cnt = 0U;
memset(band_txt[i].gprmc, 0, 256);
memset(band_txt[i].gpid, 0, 256);
band_txt[i].is_gps_sent = false;
band_txt[i].gps_last_time = 0;
band_txt[i].num_dv_frames = 0;
band_txt[i].num_dv_silent_frames = 0;
band_txt[i].num_bit_errors = 0;
band_txt[i].Initialize();
}
/* process configuration file */
@ -2634,8 +2421,11 @@ bool CQnetGateway::Init(char *cfgfile)
if (GATEWAY_SEND_QRGS_MAP)
qrgs_and_maps();
for (int i=0; i<4; i++)
Sd[i].Init();
for (int i=0; i<3; i++) {
sdin[i].Init();
sdout[i].Init();
}
sdin[3].Init();
return false;
}
@ -2685,209 +2475,6 @@ CQnetGateway::~CQnetGateway()
printf("QnetGateway exiting\n");
}
bool CQnetGateway::validate_csum(SBANDTXT &bt, bool is_gps)
{
const char *name = is_gps ? "GPS" : "GPRMC";
char *s = is_gps ? bt.gpid : bt.gprmc;
char *p = strrchr(s, '*');
if (!p) {
// BAD news, something went wrong
printf("Missing asterisk before checksum in %s\n", name);
bt.gprmc[0] = bt.gpid[0] = '\0';
return true;
} else {
*p = '\0';
// verify csum in GPRMC
bool ok = verify_gps_csum(s + 1, p + 1);
if (!ok) {
printf("csum in %s not good\n", name);
bt.gprmc[0] = bt.gpid[0] = '\0';
return true;
}
}
return false;
}
void CQnetGateway::gps_send(short int rptr_idx)
{
time_t tnow = 0;
static char old_mycall[CALL_SIZE + 1] = { " " };
if ((rptr_idx < 0) || (rptr_idx > 2)) {
printf("ERROR in gps_send: rptr_idx %d is invalid\n", rptr_idx);
return;
}
if (band_txt[rptr_idx].gprmc[0] == '\0') {
band_txt[rptr_idx].gpid[0] = '\0';
printf("missing GPS ID\n");
return;
}
if (band_txt[rptr_idx].gpid[0] == '\0') {
band_txt[rptr_idx].gprmc[0] = '\0';
printf("Missing GPSRMC\n");
return;
}
if (memcmp(band_txt[rptr_idx].gpid, band_txt[rptr_idx].lh_mycall, CALL_SIZE) != 0) {
printf("MYCALL [%s] does not match first 8 characters of GPS ID [%.8s]\n", band_txt[rptr_idx].lh_mycall, band_txt[rptr_idx].gpid);
band_txt[rptr_idx].gprmc[0] = '\0';
band_txt[rptr_idx].gpid[0] = '\0';
return;
}
/* if new station, reset last time */
if (strcmp(old_mycall, band_txt[rptr_idx].lh_mycall) != 0) {
strcpy(old_mycall, band_txt[rptr_idx].lh_mycall);
band_txt[rptr_idx].gps_last_time = 0;
}
/* do NOT process often */
time(&tnow);
if ((tnow - band_txt[rptr_idx].gps_last_time) < 31)
return;
printf("GPRMC=[%s]\n", band_txt[rptr_idx].gprmc);
printf("GPS id=[%s]\n",band_txt[rptr_idx].gpid);
if (validate_csum(band_txt[rptr_idx], false)) // || validate_csum(band_txt[rptr_idx], true))
return;
/* now convert GPS into APRS and send it */
build_aprs_from_gps_and_send(rptr_idx);
band_txt[rptr_idx].is_gps_sent = true;
time(&(band_txt[rptr_idx].gps_last_time));
return;
}
void CQnetGateway::build_aprs_from_gps_and_send(short int rptr_idx)
{
char buf[512];
const char *delim = ",";
char *saveptr = NULL;
/*** dont care about the rest */
strcpy(buf, band_txt[rptr_idx].lh_mycall);
char *p = strchr(buf, ' ');
if (p) {
if (band_txt[rptr_idx].lh_mycall[7] != ' ') {
*p = '-';
*(p + 1) = band_txt[rptr_idx].lh_mycall[7];
*(p + 2) = '>';
*(p + 3) = '\0';
} else {
*p = '>';
*(p + 1) = '\0';
}
} else
strcat(buf, ">");
strcat(buf, "APDPRS,DSTAR*,qAR,");
strcat(buf, Rptr.mod[rptr_idx].call.c_str());
strcat(buf, ":!");
//GPRMC =
strtok_r(band_txt[rptr_idx].gprmc, delim, &saveptr);
//time_utc =
strtok_r(NULL, delim, &saveptr);
//nav =
strtok_r(NULL, delim, &saveptr);
char *lat_str = strtok_r(NULL, delim, &saveptr);
char *lat_NS = strtok_r(NULL, delim, &saveptr);
char *lon_str = strtok_r(NULL, delim, &saveptr);
char *lon_EW = strtok_r(NULL, delim, &saveptr);
if (lat_str && lat_NS) {
if ((*lat_NS != 'N') && (*lat_NS != 'S')) {
printf("Invalid North or South indicator in latitude\n");
return;
}
if (strlen(lat_str) > 9) {
printf("Invalid latitude\n");
return;
}
if (lat_str[4] != '.') {
printf("Invalid latitude\n");
return;
}
lat_str[7] = '\0';
strcat(buf, lat_str);
strcat(buf, lat_NS);
} else {
printf("Invalid latitude\n");
return;
}
/* secondary table */
strcat(buf, "/");
if (lon_str && lon_EW) {
if ((*lon_EW != 'E') && (*lon_EW != 'W')) {
printf("Invalid East or West indicator in longitude\n");
return;
}
if (strlen(lon_str) > 10) {
printf("Invalid longitude\n");
return;
}
if (lon_str[5] != '.') {
printf("Invalid longitude\n");
return;
}
lon_str[8] = '\0';
strcat(buf, lon_str);
strcat(buf, lon_EW);
} else {
printf("Invalid longitude\n");
return;
}
/* Just this symbolcode only */
strcat(buf, "/");
strncat(buf, band_txt[rptr_idx].gpid + 13, 32);
// printf("Built APRS from old GPS mode=[%s]\n", buf);
strcat(buf, "\r\n");
if (aprs->aprs_sock.Write((unsigned char *)buf, strlen(buf))) {
if ((errno == EPIPE) || (errno == ECONNRESET) || (errno == ETIMEDOUT) || (errno == ECONNABORTED) ||
(errno == ESHUTDOWN) || (errno == EHOSTUNREACH) || (errno == ENETRESET) || (errno == ENETDOWN) ||
(errno == ENETUNREACH) || (errno == EHOSTDOWN) || (errno == ENOTCONN)) {
printf("build_aprs_from_gps_and_send: APRS_HOST closed connection, error=%d\n", errno);
aprs->aprs_sock.Close();
} else
printf("build_aprs_from_gps_and_send: send error=%d\n", errno);
}
return;
}
bool CQnetGateway::verify_gps_csum(char *gps_text, char *csum_text)
{
short computed_csum = 0;
char computed_csum_text[16];
short int len = strlen(gps_text);
for (short int i=0; i<len; i++) {
char c = gps_text[i];
if (computed_csum == 0)
computed_csum = (char)c;
else
computed_csum = computed_csum ^ ((char)c);
}
sprintf(computed_csum_text, "%02X", computed_csum);
// printf("computed_csum_text=[%s]\n", computed_csum_text);
char *p = strchr(csum_text, ' ');
if (p)
*p = '\0';
if (strcmp(computed_csum_text, csum_text) == 0)
return true;
else
return false;
}
int main(int argc, char **argv)
{
printf("VERSION %s\n", GW_VERSION.c_str());

@ -51,29 +51,29 @@ using STOREPEATER = struct torepeater_tag {
using SBANDTXT = struct band_txt_tag {
unsigned short streamID;
unsigned char flags[3];
char lh_mycall[CALL_SIZE + 1];
char lh_sfx[5];
char lh_yrcall[CALL_SIZE + 1];
char lh_rpt1[CALL_SIZE + 1];
char lh_rpt2[CALL_SIZE + 1];
time_t last_time;
char txt[64]; // Only 20 are used
unsigned short txt_cnt;
bool sent_key_on_msg;
std::string dest_rptr;
// try to process GPS mode: GPRMC and ID
char temp_line[256];
unsigned short temp_line_cnt;
char gprmc[256];
char gpid[256];
bool is_gps_sent;
time_t gps_last_time;
std::string mycall, sfx, urcall, rpt1, rpt2, txt, dest_rptr;
time_t last_time, gps_last_time;
bool sent_key_on_msg, is_gps_sent;
int num_dv_frames;
int num_dv_silent_frames;
int num_bit_errors;
void Initialize()
{
streamID = 0x0U;
last_time = gps_last_time = 0;
is_gps_sent = sent_key_on_msg = false;
num_dv_frames = num_dv_silent_frames = num_bit_errors = 0;
flags[0] = flags[1] = flags[2] = 0x0U;
mycall.clear();
sfx.clear();
urcall.clear();
rpt1.clear();
rpt2.clear();
txt.clear();
dest_rptr.clear();
}
};
using SSD = struct sd_tag {
@ -87,9 +87,6 @@ using SSD = struct sd_tag {
void Init() { ih = im = ig = 0; first = true; }
};
class CQnetGateway : public CKRBase
{
public:
@ -103,11 +100,7 @@ private:
int link_family[3] = { AF_UNSPEC, AF_UNSPEC, AF_UNSPEC };
// network type
int af_family[2] = { AF_UNSPEC, AF_UNSPEC };
// text stuff
bool new_group[3] = { true, true, true };
short to_print[3] = { 0, 0, 0 };
bool ABC_grp[3] = { false, false, false };
bool C_seen[3] = { false, false, false };
int Index[3] = { -1, -1, -1 };
SPORTIP g2_external, g2_ipv6_external, ircddb[2];
@ -172,6 +165,11 @@ private:
// for bit error rate calcs
CDStarDecode decode;
// g2 data
std::string lhcallsign[3], lhsfx[3];
unsigned char nextctrl[3] = { 0U, 0U, 0U };
std::string superframe[3];
// dtmf stuff
int dtmf_buf_count[3];
char dtmf_buf[3][MAX_DTMF_BUF + 1];
@ -190,9 +188,11 @@ private:
bool Printable(unsigned char *string);
void ProcessTimeouts();
void ProcessSlowData(unsigned char *data, const unsigned short sid);
void ProcessIncomingSD(const SDSVT &dsvt);
void ProcessIncomingSD(const SDSVT &dsvt, const int source_sock);
void ProcessOutGoingSD(const SDSVT &dsvt);
bool ProcessG2Msg(const unsigned char *data, const int mod, std::string &smrtgrp);
void ProcessG2(const ssize_t g2buflen, SDSVT &g2buf, const int sock_source);
void ProcessG2Header(const SDSVT &g2buf, const int source_sock);
void ProcessModem(const ssize_t len, SDSVT &dsvt);
bool Flag_is_ok(unsigned char flag);
void UnpackCallsigns(const std::string &str, std::set<std::string> &set, const std::string &delimiters = ",");
@ -202,16 +202,10 @@ private:
// read configuration file
bool ReadConfig(char *);
/* aprs functions, borrowed from my retired IRLP node 4201 */
void gps_send(short int rptr_idx);
bool verify_gps_csum(char *gps_text, char *csum_text);
void build_aprs_from_gps_and_send(short int rptr_idx);
void qrgs_and_maps();
void set_dest_rptr(const char mod, std::string &call);
bool validate_csum(SBANDTXT &bt, bool is_gps);
// for incoming slow header stuff;
SSD Sd[4];
SSD sdin[4], sdout[3];
};

@ -185,7 +185,7 @@ bool CTCPReaderWriterClient::Write(const unsigned char *buffer, const unsigned i
ssize_t ret = send(m_fd, (char *)buffer, length, 0);
if (ret != ssize_t(length)) {
if (ret < 0)
fprintf(stderr, "Error returned from send, err=%d\n", errno);
fprintf(stderr, "Error returned from send, err=%s\n", strerror(errno));
else
fprintf(stderr, "Error only wrote %d of %d bytes\n", int(ret), int(length));
return true;

Loading…
Cancel
Save

Powered by TurnKey Linux.