1 // ----------------------------------------------------------------------------
2 // pskrep.cxx
3 //
4 // Copyright (C) 2008-2009
5 // Stelios Bounanos, M0GLD
6 //
7 // This is a client for N1DQ's PSK Automatic Propagation Reporter
8 // (see http://pskreporter.info/). Philip Gladstone, N1DQ, is
9 // thanked for his helpful explanation of the protocol.
10 //
11 //
12 // This file is part of fldigi.
13 //
14 // fldigi is free software; you can redistribute it and/or modify
15 // it under the terms of the GNU General Public License as published by
16 // the Free Software Foundation; either version 3 of the License, or
17 // (at your option) any later version.
18 //
19 // fldigi is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 // ----------------------------------------------------------------------------
27
28 #include <config.h>
29
30 #ifdef __MINGW32__
31 # include "compat.h"
32 #endif
33
34 #if HAVE_SYS_UTSNAME_H
35 # include <sys/utsname.h>
36 #endif
37
38 #include <sys/time.h>
39 #if HAVE_ARPA_INET_H
40 # include <arpa/inet.h>
41 #endif
42
43 #include <stdint.h>
44 #include <cstring>
45 #include <cstdlib>
46
47 #include <string>
48 #include <sstream>
49
50 #include <deque>
51 #include <vector>
52 #include <algorithm>
53 #include <fstream>
54
55 #if GCC_VER_OK
56 # if HAVE_STD_HASH
57 # define MAP_TYPE std::unordered_map
58 # define HASH_TYPE std::hash
59 # include <unordered_map>
60 # else
61 # if HAVE_STD_TR1_HASH
62 # define MAP_TYPE std::tr1::unordered_map
63 # define HASH_TYPE std::tr1::hash
64 # include <tr1/unordered_map>
65 # endif
66 # endif
67 #else
68 // use the non-standard gnu hash_map on gcc < 4.1.0
69 // which has a broken tr1::unordered_map::operator=
70 # define MAP_TYPE __gnu_cxx::hash_map
71 # define HASH_TYPE __gnu_cxx::hash
72 # include <ext/hash_map>
73 namespace __gnu_cxx {
74 // define the missing hash specialisation for std::string
75 // using the 'const char*' hash function
76 template<> struct hash<std::string> {
operator ()__gnu_cxx::hash77 size_t operator()(const std::string& s) const { return __stl_hash_string(s.c_str()); }
78 };
79 }
80 #endif
81
82 #include <FL/Fl.H>
83
84 #include "socket.h"
85 #include "re.h"
86 #include "debug.h"
87 #include "util.h"
88 #include "trx.h"
89 #include "fl_digi.h"
90 #include "main.h"
91 #include "configuration.h"
92 #include "globals.h"
93 #include "spot.h"
94
95 #include "pskrep.h"
96
97 LOG_FILE_SOURCE(debug::LOG_SPOTTER);
98
99 //------------------------------------------------------------------------------
100 #include "threads.h"
101
102 static pthread_mutex_t pskrep_mutex = PTHREAD_MUTEX_INITIALIZER;
103 //------------------------------------------------------------------------------
104
105 // -------------------------------------------------------------------------------------------------
106
107 // Try to flush the report queue every SEND_INTERVAL seconds.
108 #define SEND_INTERVAL 300
109
110 // Ignore reports that are less than DUP_INTERVAL seconds older than
111 // a previously sent report for the same callsign and frequency band.
112 // Sent reports are also garbage-collected after DUP_INTERVAL seconds.
113 #define DUP_INTERVAL 1800
114
115 // The first TEMPLATE_THRESHOLD packets will contain the long templates;
116 // the next TEMPLATE_THRESHOLD packets will include the short templates
117 #define TEMPLATE_THRESHOLD 3
118 // Resend short templates every TEMPLATE_INTERVAL seconds
119 #define TEMPLATE_INTERVAL 1800
120
121 // Maximum send size
122 #define DGRAM_MAX (1500-14-24-8)
123
124 #define PSKREP_QUEUE_FILE "pskrqueue.txt"
125 #define PSKREP_ID_FILE "pskrkey.txt"
126
127 // -------------------------------------------------------------------------------------------------
128
129 using namespace std;
130
131 enum status_t { PSKR_STATUS_NEW, PSKR_STATUS_PENDING, PSKR_STATUS_SENT };
132 enum rtype_t { PSKREP_AUTO = 1, PSKREP_LOG = 2, PSKREP_MANUAL = 3 };
133
134 struct rcpt_report_t
135 {
rcpt_report_trcpt_report_t136 rcpt_report_t(trx_mode m = 0, long long f = 0, time_t t = 0,
137 rtype_t p = PSKREP_AUTO, string loc = "")
138 : mode(m), freq(f), rtime(t), rtype(p),
139 status(PSKR_STATUS_NEW), locator(loc) { }
140
141 trx_mode mode;
142 long long freq;
143 time_t rtime;
144 rtype_t rtype;
145
146 status_t status;
147
148 string locator;
149 };
150
151 // A band_map_t holds a list of reception reports (for a particular callsign and band)
152 typedef deque<rcpt_report_t> band_map_t;
153 // A call_map_t holds reception reports for a particular callsign
154 typedef MAP_TYPE<band_t, band_map_t, HASH_TYPE<int> > call_map_t;
155 // A container of this type holds all reception reports, sorted by callsign and band
156 typedef MAP_TYPE<string, call_map_t> queue_t;
157
158 class pskrep_sender
159 {
160 public:
161 pskrep_sender(const string& call, const string& loc, const string& ant,
162 const string& host_, const string& port_,
163 const string& long_id_, const string& short_id_);
164 ~pskrep_sender();
165
166 bool append(const string& callsign, const band_map_t::value_type& r);
167 bool send(void);
168
169 private:
170 void write_station_info(void);
171 void write_preamble(void);
172
173 string recv_callsign, recv_locator, recv_antenna;
174 string host, port;
175 string long_id, short_id;
176
177 static const unsigned char long_station_info_template[];
178 static const unsigned char short_station_info_template[];
179 static const unsigned char rcpt_record_template[];
180 vector<unsigned char> long_station_info;
181 vector<unsigned char> short_station_info;
182
183 //uint32_t identifier;
184 uint32_t sequence_number;
185
186 unsigned template_count;
187 time_t last_template;
188
189 Socket* send_socket;
190 unsigned char* dgram;
191 size_t dgram_size;
192 size_t report_offset;
193
194 void create_socket(void);
195 pthread_t resolver_thread;
196 static void* resolver(void* obj);
197
198 static const char hexsym[];
199
200 static size_t pad(size_t len, size_t mult);
201 };
202
203
204 class pskrep
205 {
206 public:
207 pskrep(const string& call, const string& loc, const string& ant,
208 const string& host, const string& port,
209 const string& long_id, const string& short_id,
210 bool reg_auto, bool reg_log, bool reg_manual);
211 ~pskrep();
212
213 static void recv(trx_mode mode, int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj);
214 static void log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
215 static void manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj);
216 bool progress(void);
count(void)217 unsigned count(void) { return new_count; }
218
219 static fre_t locator_re;
220 private:
221
222 void append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype);
223 void gc(void);
224
225 void load_queue(void);
226 void save_queue(void);
227
not_sent(const band_map_t::value_type & r)228 static bool not_sent(const band_map_t::value_type& r)
229 {
230 return r.status != PSKR_STATUS_SENT;
231 }
232
233 queue_t queue;
234 pskrep_sender sender;
235 unsigned new_count;
236 };
237
238 fre_t pskrep::locator_re("[a-r]{2}[0-9]{2}[a-x]{2}", REG_EXTENDED | REG_NOSUB | REG_ICASE);
239
240 #define SHORT_ID_SIZE 4
241 #define LONG_ID_SIZE 8
242
243 // -------------------------------------------------------------------------------------------------
244
245 static string error_string;
246
pskrep_check(void)247 static bool pskrep_check(void)
248 {
249 struct {
250 const string* var;
251 const char* msg;
252 } check[] = {
253 { &progdefaults.myCall, "callsign" },
254 { &progdefaults.myLocator, "locator" },
255 { &progdefaults.myAntenna, "antenna info" },
256 };
257 for (size_t i = 0; i < sizeof(check)/sizeof(*check); i++) {
258 if (check[i].var->empty()) {
259 error_string.assign("Error: missing ").append(check[i].msg);
260 return false;
261 }
262 }
263 if (!pskrep::locator_re.match(progdefaults.myLocator.c_str())) {
264 error_string = "Error: bad Maidenhead locator\ncheck Configure->Operator->Locator";
265 return false;
266 }
267
268 return true;
269 }
270
pskrep_error(void)271 const char* pskrep_error(void)
272 {
273 return error_string.c_str();
274 }
275
pskrep_progress(void * obj)276 static void pskrep_progress(void* obj)
277 {
278 if (reinterpret_cast<pskrep*>(obj)->progress())
279 Fl::add_timeout(SEND_INTERVAL, pskrep_progress, obj);
280 else
281 pskrep_stop();
282 }
283
pskrep_make_id(string & id,size_t len)284 static void pskrep_make_id(string& id, size_t len)
285 {
286 id.resize(len);
287
288 ifstream f("/dev/urandom");
289 if (f) {
290 for (size_t i = 0; i < len; i++)
291 while ((id[i] = f.get()) != EOF && !isgraph(id[i]));
292 f.close();
293 }
294 else {
295 unsigned seed = time(NULL);
296 if (!progdefaults.myCall.empty())
297 seed ^= simple_hash_str((const unsigned char*)progdefaults.myCall.c_str());
298 srand(seed);
299 for (size_t i = 0; i < len; i++)
300 while (!isgraph(id[i] = rand() % 0x7F));
301 }
302 }
303
304 static pskrep* pskr = 0;
305
pskrep_start(void)306 bool pskrep_start(void)
307 {
308 if (pskr)
309 return true;
310 else if (!pskrep_check())
311 return false;
312
313 // get identifier
314 string fname = TempDir;
315 fname.append(PSKREP_ID_FILE);
316 ifstream in(fname.c_str());
317 string long_id, short_id;
318 if (in)
319 in >> long_id >> short_id;
320 if (!in || in.eof()) {
321 in.close();
322 pskrep_make_id(long_id, LONG_ID_SIZE);
323 pskrep_make_id(short_id, SHORT_ID_SIZE);
324
325 ofstream out(fname.c_str());
326 if (out)
327 out << long_id << ' ' << short_id << '\n';
328 else
329 LOG_ERROR("Could not write identifiers (\"%s\", \"%s\") to %s",
330 long_id.c_str(), short_id.c_str(), fname.c_str());
331 }
332
333 pskr = new pskrep(progdefaults.myCall, progdefaults.myLocator, progdefaults.myAntenna,
334 progdefaults.pskrep_host, progdefaults.pskrep_port, long_id, short_id,
335 progdefaults.pskrep_auto, progdefaults.pskrep_log, true);
336 Fl::add_timeout(SEND_INTERVAL, pskrep_progress, pskr);
337
338 return true;
339 }
340
pskrep_stop(void)341 void pskrep_stop(void)
342 {
343 Fl::remove_timeout(pskrep_progress, pskr);
344
345 delete pskr;
346 pskr = 0;
347 }
348
pskrep_count(void)349 unsigned pskrep_count(void)
350 {
351 return pskr ? pskr->count() : 0;
352 }
353
354 // -------------------------------------------------------------------------------------------------
355
pskrep(const string & call,const string & loc,const string & ant,const string & host,const string & port,const string & long_id,const string & short_id,bool reg_auto,bool reg_log,bool reg_manual)356 pskrep::pskrep(const string& call, const string& loc, const string& ant,
357 const string& host, const string& port,
358 const string& long_id, const string& short_id,
359 bool reg_auto, bool reg_log, bool reg_manual)
360 : sender(call, loc, ant, host, port, long_id, short_id), new_count(0)
361 {
362 if (reg_auto)
363 spot_register_recv(pskrep::recv, this, PSKREP_RE, REG_EXTENDED | REG_ICASE);
364 if (reg_log)
365 spot_register_log(pskrep::log, this);
366 if (reg_manual)
367 spot_register_manual(pskrep::manual, this);
368 load_queue();
369 }
370
~pskrep()371 pskrep::~pskrep()
372 {
373 spot_unregister_recv(pskrep::recv, this);
374 spot_unregister_log(pskrep::log, this);
375 spot_unregister_manual(pskrep::manual, this);
376 save_queue();
377 }
378
379 // This function is called by spot_recv() when its buffer matches our PSKREP_RE
recv(trx_mode mode,int afreq,const char * str,const regmatch_t * calls,size_t len,void * obj)380 void pskrep::recv(trx_mode mode, int afreq, const char* str, const regmatch_t* calls, size_t len, void* obj)
381 {
382 if (unlikely(calls[PSKREP_RE_INDEX].rm_so == -1 || calls[PSKREP_RE_INDEX].rm_eo == -1))
383 return;
384
385 string call(str + calls[PSKREP_RE_INDEX].rm_so, calls[PSKREP_RE_INDEX].rm_eo - calls[PSKREP_RE_INDEX].rm_so);
386 long long freq = afreq;
387 if (!wf->USB())
388 freq = -freq;
389 freq += wf->rfcarrier();
390 LOG_DEBUG("Spotted \"%s\" in buffer \"%s\"", call.c_str(), str);
391
392 reinterpret_cast<pskrep*>(obj)->append(call.c_str(), "", freq,
393 active_modem->get_mode(), time(NULL), PSKREP_AUTO);
394 }
395
396 // This function is called by spot_log()
log(const char * call,const char * loc,long long freq,trx_mode mode,time_t rtime,void * obj)397 void pskrep::log(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
398 {
399 reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_LOG);
400 }
401
402 // This function is called by spot_manual()
manual(const char * call,const char * loc,long long freq,trx_mode mode,time_t rtime,void * obj)403 void pskrep::manual(const char* call, const char* loc, long long freq, trx_mode mode, time_t rtime, void* obj)
404 {
405 reinterpret_cast<pskrep*>(obj)->append(call, loc, freq, mode, rtime, PSKREP_MANUAL);
406 }
407
append(string call,const char * loc,long long freq,trx_mode mode,time_t rtime,rtype_t rtype)408 void pskrep::append(string call, const char* loc, long long freq, trx_mode mode, time_t rtime, rtype_t rtype)
409 {
410 if (unlikely(call.empty()))
411 return;
412 transform(call.begin(), call.end(), call.begin(), static_cast<int (*)(int)>(toupper));
413
414 if (!progdefaults.pskrep_qrg)
415 freq = 0LL;
416
417 if (*loc && !locator_re.match(loc))
418 loc = "";
419
420 band_map_t& bandq = queue[call][band(freq)];
421 if (bandq.empty() || rtime - bandq.back().rtime >= DUP_INTERVAL) { // add new
422 bandq.push_back(rcpt_report_t(mode, freq, rtime, rtype, loc));
423 LOG_VERBOSE("Added (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%d, time=%d, type=%u)",
424 call.c_str(), loc, mode_info[mode].adif_name,
425 static_cast<int>(freq),
426 static_cast<int>(rtime), rtype);
427 new_count++;
428 save_queue();
429 }
430 else if (!bandq.empty()) {
431 band_map_t::value_type& r = bandq.back();
432 if (r.status != PSKR_STATUS_SENT && *loc && r.locator != loc) { // update last
433 r.locator = loc;
434 r.rtype = rtype;
435 LOG_VERBOSE("Updated (call=\"%s\", loc=\"%s\", mode=\"%s\", freq=%d, time=%d, type=%u)",
436 call.c_str(), loc, mode_info[r.mode].adif_name,
437 static_cast<int>(r.freq),
438 static_cast<int>(r.rtime), rtype);
439 save_queue();
440 }
441 }
442 }
443
444 // Handle queued reports
progress(void)445 bool pskrep::progress(void)
446 {
447 if (queue.empty())
448 return true;
449
450 unsigned nrep = 0;
451 bool sender_full = false;
452 for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i) {
453 for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j) {
454 for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k) {
455 switch (k->status) {
456 case PSKR_STATUS_NEW:
457 if ((sender_full = !sender.append(i->first, *k)))
458 goto send_reports;
459 k->status = PSKR_STATUS_PENDING;
460 nrep++;
461 break;
462 case PSKR_STATUS_PENDING: // sent in last cycle
463 k->status = PSKR_STATUS_SENT;
464 default:
465 break;
466 }
467 }
468 }
469 }
470
471 send_reports:
472 LOG_VERBOSE("Found %u new report(s)", nrep);
473 if (nrep) {
474 if (!sender.send()) {
475 LOG_ERROR("Sender failed, disabling pskreporter");
476 return false;
477 }
478 return progress();
479 }
480
481 gc();
482 save_queue();
483 return true;
484 }
485
486 // Delete sent reports that are older than DUP_INTERVAL seconds
gc(void)487 void pskrep::gc(void)
488 {
489 time_t threshold = time(NULL) - DUP_INTERVAL;
490 unsigned rm = 0;
491
492 for (queue_t::iterator i = queue.begin(); i != queue.end() ; ) {
493 for (call_map_t::iterator j = i->second.begin(); j != i->second.end() ; ) {
494 band_map_t& b = j->second;
495 band_map_t::iterator k = find_if(b.begin(), b.end(), not_sent);
496 if (k != b.begin() && k == b.end())
497 --k;
498 rm += k - b.begin();
499 k = b.erase(b.begin(), k);
500
501 if (k != b.end() && k->status == PSKR_STATUS_SENT && k->rtime <= threshold) {
502 b.erase(k);
503 rm++;
504 }
505
506 if (b.empty())
507 i->second.erase(j++);
508 else
509 ++j;
510 }
511 if (i->second.empty())
512 queue.erase(i++);
513 else
514 ++i;
515 }
516
517 LOG_DEBUG("Removed %u sent report(s)", rm);
518 }
519
520 static ostream& operator<<(ostream& out, const rcpt_report_t& r);
521 static istream& operator>>(istream& in, rcpt_report_t& r);
522 static ostream& operator<<(ostream& out, const queue_t& q);
523 static istream& operator>>(istream& in, queue_t& q);
524
save_queue(void)525 void pskrep::save_queue(void)
526 {
527 string fname = TempDir;
528 fname.append(PSKREP_QUEUE_FILE);
529 ofstream out(fname.c_str());
530
531 if (out)
532 out << queue;
533 else
534 LOG_ERROR("Could not write %s", fname.c_str());
535 }
536
load_queue(void)537 void pskrep::load_queue(void)
538 {
539 string fname = TempDir;
540 fname.append(PSKREP_QUEUE_FILE);
541 ifstream in(fname.c_str());
542 if (!in)
543 return;
544
545 in >> queue;
546 // restore pending reports as new
547 for (queue_t::iterator i = queue.begin(); i != queue.end(); ++i)
548 for (call_map_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
549 for (band_map_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
550 if (k->status == PSKR_STATUS_PENDING)
551 k->status = PSKR_STATUS_NEW;
552 }
553
554 // -------------------------------------------------------------------------------------------------
555
556 // Text fields must be <= 254 bytes
557 #define MAX_TEXT_SIZE 254
558 // Records must be padded to a multiple of 4
559 #define PAD 4
560
pskrep_sender(const string & call,const string & loc,const string & ant,const string & host_,const string & port_,const string & long_id_,const string & short_id_)561 pskrep_sender::pskrep_sender(const string& call, const string& loc, const string& ant,
562 const string& host_, const string& port_,
563 const string& long_id_, const string& short_id_)
564 : recv_callsign(call, 0, MAX_TEXT_SIZE), recv_locator(loc, 0, MAX_TEXT_SIZE),
565 recv_antenna(ant, 0, MAX_TEXT_SIZE),
566 host(host_, 0, MAX_TEXT_SIZE), port(port_, 0, MAX_TEXT_SIZE),
567 long_id(long_id_, 0, LONG_ID_SIZE), short_id(short_id_, 0, SHORT_ID_SIZE),
568 sequence_number(0), template_count(2 * TEMPLATE_THRESHOLD), last_template(0),
569 send_socket(0), dgram_size(0), report_offset(0)
570 {
571 create_socket();
572 dgram = new unsigned char[DGRAM_MAX];
573 write_station_info();
574 }
575
~pskrep_sender()576 pskrep_sender::~pskrep_sender()
577 {
578 delete send_socket;
579 delete [] dgram;
580 }
581
582 // fldigi uses 0x0219 as the long station info template id (bytes 4,5)
583 const unsigned char pskrep_sender::long_station_info_template[] = {
584 0x00, 0x03, 0x00, 0x34, 0x02, 0x19, 0x00, 0x05, 0x00, 0x00,
585 0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
586 0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
587 0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
588 0x80, 0x08, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // decoderSoftware
589 0x80, 0x09, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // anntennaInformation
590 0x00, 0x00
591 };
592
593 // fldigi uses 0x0218 as the short station info template id (bytes 4,5)
594 const unsigned char pskrep_sender::short_station_info_template[] = {
595 0x00, 0x03, 0x00, 0x24, 0x02, 0x18, 0x00, 0x03, 0x00, 0x00,
596 0x80, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverCallsign
597 0x80, 0x04, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // receiverLocator
598 0x80, 0x0C, 0x00, 0x08, 0x00, 0x00, 0x76, 0x8F, // persistentIdentifier
599 0x00, 0x00
600 };
601
write_station_info(void)602 void pskrep_sender::write_station_info(void)
603 {
604 char prog_info[MAX_TEXT_SIZE];
605 size_t prog_len;
606
607 prog_len = snprintf(prog_info, sizeof(prog_info), "%s", PACKAGE_TARNAME "-" PACKAGE_VERSION);
608 prog_len = MIN(prog_len, sizeof(prog_info));
609 struct utsname u;
610 if (uname(&u) != -1) {
611 prog_len += snprintf(prog_info+prog_len, sizeof(prog_info)-prog_len, "/%s-%s", u.sysname, u.machine);
612 prog_len = MIN(prog_len, sizeof(prog_info));
613 }
614
615 size_t call_len = recv_callsign.length(),
616 loc_len = recv_locator.length(),
617 ant_len = recv_antenna.length();
618
619 size_t long_len, short_len;
620 // Long station info length
621 long_len = 4 + // 4-byte header
622 1 + call_len + // 1-byte call length + call string length
623 1 + loc_len + // 1-byte loc length + loc string length
624 8 + // 8-byte identifier
625 1 + prog_len + // 1-byte prog length + prog string length
626 1 + ant_len; // 1-byte ant length + ant string length
627 long_len = pad(long_len, PAD);
628 // Short station info length
629 short_len = 4 + // 4-byte header
630 1 + call_len + // 1-byte call length + call string length
631 1 + loc_len + // 1-byte loc length + loc string length
632 8; // 8-byte identifier
633 short_len = pad(short_len, PAD);
634
635 long_station_info.resize(long_len);
636 short_station_info.resize(short_len);
637 unsigned char* p;
638 size_t npad;
639
640 // Write the long station info
641 p = &long_station_info[0];
642 // header
643 memcpy(p, long_station_info_template + 4, 2); p += 2;
644 *reinterpret_cast<uint16_t*>(p) = htons(long_len); p += sizeof(uint16_t);
645 // call
646 *p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
647 // locator
648 *p++ = loc_len; memcpy(p, recv_locator.data(), loc_len); p += loc_len;
649 // identifier
650 memcpy(p, long_id.data(), LONG_ID_SIZE); p += LONG_ID_SIZE;
651 // program
652 *p++ = prog_len; memcpy(p, prog_info, prog_len); p += prog_len;
653 // antenna
654 *p++ = ant_len; memcpy(p, recv_antenna.data(), ant_len); p += ant_len;
655 // pad
656 npad = &long_station_info[0] + long_len - p;
657 if (npad)
658 memset(p, 0, npad);
659 LOG_DEBUG("long_station_info=\"%s\"", str2hex(&long_station_info[0], long_len));
660
661 // Write the short station info
662 p = &short_station_info[0];
663 // header
664 memcpy(p, short_station_info_template + 4, 2); p += 2;
665 *reinterpret_cast<uint16_t*>(p) = htons(short_len); p += sizeof(uint16_t);
666 // call
667 *p++ = call_len; memcpy(p, recv_callsign.data(), call_len); p += call_len;
668 // locator
669 *p++ = loc_len; memcpy(p, recv_locator.data(), loc_len); p += loc_len;
670 // identifier
671 memcpy(p, long_id.data(), LONG_ID_SIZE); p += LONG_ID_SIZE;
672 // pad
673 npad = &short_station_info[0] + short_len - p;
674 if (npad)
675 memset(p, 0, npad);
676 LOG_DEBUG("short_station_info=\"%s\"", str2hex(&short_station_info[0], short_len));
677 }
678
679 // fldigi uses 0x022C as the reception record template id (bytes 4,5)
680 const unsigned char pskrep_sender::rcpt_record_template[] = {
681 0x00, 0x02, 0x00, 0x34, 0x02, 0x2C, 0x00, 0x06,
682 0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // senderCallsign
683 0x00, 0x96, 0x00, 0x04, // flowStartSeconds
684 0x80, 0x05, 0x00, 0x04, 0x00, 0x00, 0x76, 0x8F, // frequency
685 0x80, 0x0A, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F, // mode (adif string)
686 0x80, 0x03, 0xff, 0xff, 0x00, 0x00, 0x76, 0x8F, // senderLocator (if known)
687 0x80, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F // flags (informationSource)
688 };
689
write_preamble(void)690 void pskrep_sender::write_preamble(void)
691 {
692 time_t now = time(NULL);
693 unsigned char* p = dgram;
694
695 // header
696 *p++ = 0x00; *p++ = 0x0A; /* length written later */ p += 2;
697 /* time written later */ p += sizeof(uint32_t);
698 *reinterpret_cast<uint32_t*>(p) = htonl(sequence_number); p += sizeof(uint32_t);
699 memcpy(p, short_id.data(), SHORT_ID_SIZE); p += SHORT_ID_SIZE;
700
701 const unsigned char* station_info_template(long_station_info_template);
702 size_t tlen;
703 vector<unsigned char>* station_info(&long_station_info);
704
705 if (template_count == 0 && now - last_template >= TEMPLATE_INTERVAL)
706 template_count = TEMPLATE_THRESHOLD;
707 if (template_count > TEMPLATE_THRESHOLD) {
708 station_info_template = long_station_info_template;
709 tlen = sizeof(long_station_info_template);
710 station_info = &long_station_info;
711 }
712 else if (template_count != 0) {
713 station_info_template = short_station_info_template;
714 tlen = sizeof(short_station_info_template);
715 station_info = &short_station_info;
716 }
717 if (template_count > 0) {
718 memcpy(p, rcpt_record_template, sizeof(rcpt_record_template)); p += sizeof(rcpt_record_template);
719 memcpy(p, station_info_template, tlen); p += tlen;
720 template_count--;
721 last_template = now;
722 }
723
724 // station info record
725 memcpy(p, &(*station_info)[0], station_info->size());
726 p += station_info->size();
727
728 report_offset = p - dgram;
729 // write report record header
730 memcpy(p, rcpt_record_template + 4, 2);
731 p += 2; /* length written later */
732 p += sizeof(uint16_t);
733
734 dgram_size = p - dgram;
735 }
736
append(const string & callsign,const band_map_t::value_type & r)737 bool pskrep_sender::append(const string& callsign, const band_map_t::value_type& r)
738 {
739 guard_lock dsp_lock(&pskrep_mutex);
740
741 if (dgram_size == 0)
742 write_preamble();
743
744 size_t call_len = callsign.length();
745 call_len = MIN(MAX_TEXT_SIZE, call_len);
746
747 const char* mode = mode_info[r.mode].adif_name;
748 size_t mode_len = strlen(mode);
749 mode_len = MIN(MAX_TEXT_SIZE, mode_len);
750
751 size_t loc_len = MIN(MAX_TEXT_SIZE, r.locator.length());
752
753 // call_len + call + time + freq + mode_len + mode + loc_len + loc + info
754 size_t rlen = 1 + call_len + 4 + 4 + 1 + mode_len + 1 + loc_len + 1;
755
756 if (pad(rlen, PAD) + dgram_size > DGRAM_MAX) // datagram full
757 return false;
758
759
760 LOG_INFO("Appending report (call=%s mode=%s freq=%d time=%d type=%u)",
761 callsign.c_str(), mode_info[r.mode].adif_name,
762 static_cast<int>(r.freq),
763 static_cast<int>(r.rtime), r.rtype);
764
765 unsigned char* start = dgram + dgram_size;
766 unsigned char* p = start;
767 // call
768 *p++ = call_len; memcpy(p, callsign.data(), call_len); p += call_len;
769 // 4-byte reception time
770 *reinterpret_cast<uint32_t*>(p) = htonl(r.rtime); p += sizeof(uint32_t);
771 // 4-byte freq
772 *reinterpret_cast<uint32_t*>(p) = htonl(r.freq); p += sizeof(uint32_t);
773 // mode
774 *p++ = mode_len; memcpy(p, mode, mode_len); p += mode_len;
775 // locator
776 *p++ = loc_len; memcpy(p, r.locator.data(), loc_len); p += loc_len;
777 // info source
778 *p++ = r.rtype;
779
780 LOG_DEBUG(" \"%s\"", str2hex(start, p - start));
781
782 dgram_size += rlen;
783 return true;
784 }
785
resolver(void * obj)786 void* pskrep_sender::resolver(void* obj)
787 {
788 pskrep_sender* s = reinterpret_cast<pskrep_sender*>(obj);
789 try {
790 s->send_socket = new Socket(Address(s->host.c_str(), s->port.c_str(), "udp"));
791 s->send_socket->connect();
792 }
793 catch (const SocketException& e) {
794 LOG_ERROR("Could not resolve %s: %s", s->host.c_str(), e.what());
795 }
796
797 return NULL;
798 }
799
create_socket(void)800 void pskrep_sender::create_socket(void)
801 {
802 if (pthread_create(&resolver_thread, NULL, resolver, this) != 0)
803 LOG_PERROR("pthread_create");
804 }
805
send(void)806 bool pskrep_sender::send(void)
807 {
808 if (!send_socket)
809 return false;
810
811 // empty dgram or no reports (shouldn't happen)
812 if (dgram_size == 0 || dgram_size == report_offset + 4) {
813 stringstream info;
814 info << dgram_size << " " << report_offset;
815 LOG_DEBUG("Not sending empty dgram: %s", info.str().c_str());
816 return false;
817 }
818
819 // Finish writing the report record:
820 // do we need padding?
821 size_t npad = (dgram_size - report_offset) % PAD;
822 if (npad) {
823 npad = PAD - npad;
824 memset(dgram + dgram_size, 0x0, npad);
825 dgram_size += npad;
826 }
827 // write length
828 *reinterpret_cast<uint16_t*>(dgram + report_offset + 2) = htons(dgram_size - report_offset);
829
830 // finish writing the datagram
831 *reinterpret_cast<uint16_t*>(dgram + 2) = htons(dgram_size);
832 *reinterpret_cast<uint32_t*>(dgram + 4) = htonl(time(NULL));
833
834 bool ret;
835 {
836 stringstream info;
837 info << "(" << dgram_size << "): \"" << str2hex(dgram, dgram_size) << "\"";
838 LOG_DEBUG("Sending datgram %s", info.str().c_str());
839 }
840 try {
841 if ((size_t)send_socket->send(dgram, dgram_size) != dgram_size)
842 throw SocketException("short write");
843 ret = true;
844 }
845 catch (const SocketException& e) {
846 LOG_ERROR("Could not send datagram to %s port %s: %s", host.c_str(), port.c_str(), e.what());
847 ret = false;
848 }
849
850 // increment this regardless of any errors
851 sequence_number++;
852 dgram_size = 0;
853
854 return ret;
855 }
856
857 // Pad len to a multiple of mult
pad(size_t len,size_t mult)858 size_t pskrep_sender::pad(size_t len, size_t mult)
859 {
860 size_t r = len % mult;
861 return r ? len + mult - r : len;
862 }
863
864 // -------------------------------------------------------------------------------------------------
865
operator >>(istream & in,rtype_t & t)866 static istream& operator>>(istream& in, rtype_t& t)
867 {
868 int i;
869 in >> i;
870 t = static_cast<rtype_t>(i);
871 return in;
872 }
operator >>(istream & in,status_t & t)873 static istream& operator>>(istream& in, status_t& t)
874 {
875 int i;
876 in >> i;
877 t = static_cast<status_t>(i);
878 return in;
879 }
operator >>(istream & in,rcpt_report_t & r)880 static istream& operator>>(istream& in, rcpt_report_t& r)
881 {
882 in >> r.mode >> r.freq >> r.rtime >> r.rtype >> r.status >> r.locator;
883 if (*r.locator.c_str() == '?') r.locator.clear();
884 return in;
885 }
886
operator <<(ostream & out,const rcpt_report_t & r)887 static ostream& operator<<(ostream& out, const rcpt_report_t& r)
888 {
889 return out << r.mode << ' ' << r.freq << ' ' << r.rtime << ' '
890 << r.rtype << ' ' << r.status << ' ' << (r.locator.empty() ? "?" : r.locator);
891 }
operator <<(ostream & out,const queue_t & q)892 static ostream& operator<<(ostream& out, const queue_t& q)
893 {
894 for (queue_t::const_iterator i = q.begin(); i != q.end(); ++i)
895 for (call_map_t::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
896 for (band_map_t::const_iterator k = j->second.begin(); k != j->second.end(); ++k)
897 out << *k << " " << j->first << " " << i->first << '\n';
898
899 return out;
900 }
operator >>(istream & in,queue_t & q)901 static istream& operator>>(istream& in, queue_t& q)
902 {
903 rcpt_report_t rep;
904 int band;
905 string call;
906
907 while (in >> rep >> band >> call)
908 q[call][static_cast<band_t>(band)].push_back(rep);
909
910 return in;
911 }
912