1 // =====================================================================
2 //
3 // n3fjp_logger.cxx
4 //
5 // interface to multiple n3fjp tcpip logbook services
6 //
7 // Copyright (C) 2016
8 //		Dave Freese, W1HKJ
9 //		Dave Anderson, KA3PMW
10 //
11 // This file is part of fldigi.
12 //
13 // Fldigi is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // Fldigi is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
25 // =====================================================================
26 
27 #include <iostream>
28 #include <sstream>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <time.h>
33 #include <cmath>
34 #include <cstring>
35 #include <vector>
36 #include <list>
37 #include <stdlib.h>
38 #include <time.h>
39 
40 #include <FL/Fl_Text_Display.H>
41 #include <FL/Fl_Text_Buffer.H>
42 
43 #include "threads.h"
44 #include "socket.h"
45 
46 #include "rigsupport.h"
47 #include "modem.h"
48 #include "trx.h"
49 #include "fl_digi.h"
50 #include "configuration.h"
51 #include "main.h"
52 #include "waterfall.h"
53 #include "macros.h"
54 #include "qrunner.h"
55 #include "debug.h"
56 #include "status.h"
57 #include "icons.h"
58 #include "logsupport.h"
59 #include "n3fjp_logger.h"
60 #include "confdialog.h"
61 #include "rigsupport.h"
62 #include "contest.h"
63 #include "timeops.h"
64 
65 LOG_FILE_SOURCE(debug::LOG_N3FJP);
66 
67 using namespace std;
68 
69 static void send_log_data();
70 
71 //======================================================================
72 // Socket N3FJP i/o used on all platforms
73 //======================================================================
74 
75 pthread_t n3fjp_thread;
76 pthread_t n3fjp_rx_socket_thread;
77 Socket *n3fjp_socket = 0;
78 
79 pthread_mutex_t n3fjp_mutex			= PTHREAD_MUTEX_INITIALIZER;
80 pthread_mutex_t send_this_mutex		= PTHREAD_MUTEX_INITIALIZER;
81 pthread_mutex_t report_mutex		= PTHREAD_MUTEX_INITIALIZER;
82 pthread_mutex_t n3fjp_socket_mutex	= PTHREAD_MUTEX_INITIALIZER;
83 
84 static string send_this = "";
85 static string pathname;
86 static stringstream result;
87 
88 bool n3fjp_connected = false;
89 bool n3fjp_enabled   = false;
90 bool n3fjp_exit      = false;
91 
92 string n3fjp_ip_address = "";
93 string n3fjp_ip_port    = "";
94 
95 string n3fjp_rxbuffer;
96 string connected_to;
97 
98 enum {UNKNOWN, N3FJP, FLDIGI};
99 
100 bool n3fjp_bool_add_record = false;
101 int n3fjp_has_xcvr_control = UNKNOWN;
102 
103 string tracked_freq = "";
104 int  tracked_mode = -1;
105 
106 enum {
107   FJP_NONE,
108   FJP_ACL,			// Amateur Contact Log
109   FJP_FD,			// ARRL Field Day
110   FJP_WFD,			// ARRL Winter Field Day
111   FJP_KD,			// ARRL Kids Day
112   FJP_ARR,			// ARRL Rookie Roundup
113   FJP_RTTY,			// ARRL Rtty
114   FJP_ASCR,			// ARRL School Club Roundup
115   FJP_JOTA,			// ARRL Jamboree On The Air
116   FJP_AICW,			// ARRL International DX (CW)
117   FJP_SS,			// ARRL November Sweepstakes
118   FJP_CQ_WPX,		// CQ WPX
119   FJP_CQWWRTTY,		// CQWW Rtty
120   FJP_CQWWDX,		// CQWW DX
121   FJP_IARI,			// Italian ARI International DX
122   FJP_NAQP,			// North American QSO Party
123   FJP_NAS,			// North American Sprint
124   FJP_1010,			// Ten Ten
125   FJP_AIDX,			// Africa All Mode
126   FJP_VHF,			// VHF
127   FJP_WAE,			// Worked All Europe
128   FJP_MDQP,			// MD QSOP record format
129   FJP_7QP,			// 7QP record format
130   FJP_NEQP,			// New England QSO party record format
131   FJP_QP1,			// QSO party record format 1 / 7QP contest
132   FJP_QP2,			// QSO party record format 2
133   FJP_QP3,			// QSO party record format 3
134   FJP_QP4,			// QSO party record format 4
135   FJP_QP5,			// QSO party record format 5
136   FJP_QP6			// QSO party record format 6
137 };
138 
139 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
140 //
141 // "7QP",      "B",   "B",  "B",    "",    "I",    "",     "",    "SSCCC"
142 // "New Eng",  "B",   "B",  "B",    "",    "I",    "",     "",    "CCCSS"
143 // "MD SQP"    "",    "B",  "B",    "",    "",     "",     "B",   ""
144 // "QSOP 1"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
145 // "QSOP 2"    "B",   "B",  "B",    "",    "B",    "",     "",    ""
146 // "QSOP 3"    "",    "B",  "B",    "B",   "I",    "",     "",    ""
147 // "QSOP 4"    "",    "B",  "B",    "",    "I",    "B",    "",    ""
148 // "QSOP 5"    "B",   "B",  "B",    "",    "",     "",     "",    ""
149 // "QSOP 6"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
150 
151 struct N3FJP_LOGGER {
152 	const char *program;
153 	int        contest;
154 	bool       in_state;
155 } n3fjp_logger[] = {
156 	{"No Contest", FJP_NONE, false},
157 	{"Amateur Contact Log", FJP_ACL, false},
158 	{"Africa All-Mode International", FJP_AIDX, false},
159 	{"ARRL Field Day", FJP_FD, false},
160 	{"Winter FD", FJP_WFD, false},
161 	{"ARRL International DX", FJP_AICW, false},
162 	{"Jamboree on the Air", FJP_JOTA, false},
163 	{"ARRL Kids Day", FJP_KD, false},
164 	{"ARRL Rookie Roundup", FJP_ARR, false},
165 	{"ARRL RTTY Roundup", FJP_RTTY, false},
166 	{"School Club Roundup", FJP_ASCR, false},
167 	{"ARRL November Sweepstakes", FJP_SS, false},
168 	{"BARTG RTTY Contest", FJP_NONE, false},
169 	{"CQ WPX Contest Log", FJP_CQ_WPX, false},
170 	{"CQ WW DX Contest Log", FJP_CQWWDX, false},
171 	{"CQ WW DX RTTY Contest Log", FJP_CQWWRTTY, false},
172 	{"Italian A.R.I. International DX", FJP_IARI, false},
173 	{"NAQP", FJP_NAQP, false},
174 	{"NA Sprint", FJP_NAS, false},
175 	{"Ten Ten", FJP_1010, false},
176 	{"VHF", FJP_VHF, false},
177 	{"Worked All Europe", FJP_WAE, false},
178 
179 	{"Alabama QSO Party Contest Log", FJP_QP1, true},
180 	{"ALQP Contest Log (Out of State)", FJP_QP1, false},
181 	{"Arkansas QSO Party Contest Log", FJP_QP1, true},
182 	{"ARQP Contest Log (Out of State)", FJP_QP1, false},
183 	{"British Columbia QSO Party Contest Log", FJP_QP1, true},
184 	{"BCQP Contest Log (Out of Province)", FJP_QP1, false},
185 	{"Florida QSO Party Contest Log", FJP_QP1, true},
186 	{"FLQP Contest Log (Out of State)", FJP_QP1, false},
187 	{"Georgia QSO Party Contest Log", FJP_QP1, true},
188 	{"GAQP Contest Log (Out of State)", FJP_QP1, false},
189 	{"Hawaii QSO Party Contest Log", FJP_QP1, true},
190 	{"HIQP Contest Log (Out of State)", FJP_QP1, false},
191 	{"Iowa QSO Party Contest Log", FJP_QP1, true},
192 	{"IAQP Contest Log (Out of State)", FJP_QP1, false},
193 	{"Idaho QSO Party Contest Log", FJP_QP1, true},
194 	{"IDQP Contest Log (Out of State)", FJP_QP1, false},
195 	{"Illinois QSO Party Contest Log", FJP_QP1, true},
196 	{"ILQP Contest Log (Out of State)", FJP_QP1, false},
197 	{"Indiana QSO Party Contest Log", FJP_QP1, true},
198 	{"INQP Contest Log (Out of State)", FJP_QP1, false},
199 	{"Kansas QSO Party Contest Log", FJP_QP1, true},
200 	{"KSQP Contest Log (Out of State)", FJP_QP1, false},
201 	{"Kentucky QSO Party Contest Log", FJP_QP1, true},
202 	{"KYQP Contest Log (Out of State)", FJP_QP1, false},
203 	{"Louisiana QSO Party Contest Log", FJP_QP1, true},
204 	{"LAQP Contest Log (Out of State)", FJP_QP1, false},
205 	{"Missouri QSO Party Contest Log", FJP_QP1, true},
206 	{"MOQP Contest Log (Out of State)", FJP_QP1, false},
207 	{"Mississippi QSO Party Contest Log", FJP_QP1, true},
208 	{"MSQP Contest Log (Out of State)", FJP_QP1, false},
209 	{"North Dakota QSO Party Contest Log", FJP_QP1, true},
210 	{"NDQP Contest Log (Out of State)", FJP_QP1, false},
211 	{"New Jersey QSO Party Contest Log", FJP_QP1, true},
212 	{"NJQP Contest Log (Out of State)", FJP_QP1, false},
213 
214 	{"Montana QSO Party Contest Log", FJP_QP2, true},
215 	{"MTQP Contest Log (Out of State)", FJP_QP2, false},
216 	{"Nebraska QSO Party Contest Log", FJP_QP2, true},
217 	{"NEQP Contest Log (Out of State)", FJP_QP2, false},
218 	{"New York QSO Party Contest Log", FJP_QP2, true},
219 	{"NYQP Contest Log (Out of State)", FJP_QP2, false},
220 	{"Ohio QSO Party Contest Log", FJP_QP2, true},
221 	{"OHQP Contest Log (Out of State)", FJP_QP2, false},
222 	{"Oklahoma QSO Party Contest Log", FJP_QP2, true},
223 	{"OKQP Contest Log (Out of State)", FJP_QP2, false},
224 	{"Ontario QSO Party Contest Log", FJP_QP2, true},
225 	{"ONQP Contest Log (Out of Province)", FJP_QP2, false},
226 	{"South Dakota QSO Party Contest Log", FJP_QP2, true},
227 	{"SDQP Contest Log (Out of State)", FJP_QP2, false},
228 	{"Tennessee QSO Party Contest Log", FJP_QP2, true},
229 	{"TNQP Contest Log (Out of State)", FJP_QP2, false},
230 	{"Texas QSO Party Contest Log", FJP_QP2, true},
231 	{"TXQP Contest Log (Out of State)", FJP_QP2, false},
232 	{"Vermont QSO Party Contest Log", FJP_QP2, true},
233 	{"VTQP Contest Log (Out of State)", FJP_QP2, false},
234 	{"Washington Salmon Run QSO Party Contest Log", FJP_QP2, true},
235 	{"WAQP Contest Log (Out of State)", FJP_QP2, false},
236 	{"Maine QSO Party Contest Log", FJP_QP2, true},
237 	{"MEQP Contest Log (Out of State)", FJP_QP2, false},
238 
239 	{"Arizona QSO Party Contest Log", FJP_QP3, true},
240 	{"AZQP Contest Log (Out of State)", FJP_QP3, false},
241 	{"California QSO Party Contest Log", FJP_QP3, true},
242 	{"CAQP Contest Log (Out of State)", FJP_QP3, false},
243 	{"Michigan QSO Party Contest Log", FJP_QP3, true},
244 	{"MIQP Contest Log (Out of State)", FJP_QP3, false},
245 	{"Pennsylvania QSO Party Contest Log", FJP_QP3, true},
246 	{"PAQP Contest Log (Out of State)", FJP_QP3, false},
247 	{"Virginia QSO Party Contest Log", FJP_QP3, true},
248 	{"VAQP Contest Log (Out of State)", FJP_QP3, false},
249 
250 	{"Colorado QSO Party Contest Log", FJP_QP4, true},
251 	{"COQP Contest Log (Out of State)", FJP_QP4, false},
252 	{"Maryland QSO Party Contest Log", FJP_MDQP, true},
253 	{"MDQP Contest Log (Out of State)", FJP_MDQP, false},
254 
255 	{"Minnesota QSO Party Contest Log", FJP_QP4, true},
256 	{"MNQP Contest Log (Out of State)", FJP_QP4, false},
257 	{"New Mexico QSO Party Contest Log", FJP_QP4, true},
258 	{"NMQP Contest Log (Out of State)", FJP_QP4, false},
259 	{"North Carolina QSO Party Contest Log", FJP_QP6, true},
260 	{"NCQP Contest Log (Out of State)", FJP_QP6, false},
261 
262 	{"South Carolina QSO Party Contest Log", FJP_QP5, true},
263 	{"SCQP Contest Log (Out of State)", FJP_QP5, false},
264 	{"West Virginia QSO Party Contest Log", FJP_QP5, true},
265 	{"WVQP Contest Log (Out of State)", FJP_QP5, false},
266 	{"Wisconsin QSO Party Contest Log", FJP_QP6, true},
267 	{"WIQP Contest Log (Out of State)", FJP_QP6, false},
268 
269 	{"7QP QSO Party Contest Log", FJP_7QP, true},
270 	{"7QP Contest Log (Out of Region)", FJP_7QP, false},
271 
272 	{"New England QSO Party Contest Log", FJP_NEQP, true},
273 	{"NEQP Contest Log (Out of Region)", FJP_NEQP, false}
274 };
275 
276 int  n3fjp_contest = FJP_NONE;
277 bool n3fjp_in_state = false;
278 
279 int n3fjp_wait = 0;
280 
281 void adjust_freq(string s);
282 void n3fjp_parse_response(string s);
283 void n3fjp_disp_report(string s, string fm = "", bool tofile = true);
284 void n3fjp_send(string cmd, bool tofile = true);
285 void n3fjp_rcv(string &rx, bool tofile = true);
286 void n3fjp_send_freq_mode();
287 void n3fjp_clear_record();
288 void n3fjp_getfields();
289 void n3fjp_get_record(string call);
290 static string ParseField(string &record, string fieldtag);
291 static string ParseTextField(string &record, string fieldtag);
292 static string ucasestr(string s);
293 static void n3fjp_parse_data_stream(string buffer);
294 static void n3fjp_parse_calltab_event(string buffer);
295 string fmt_date(string date);
296 string fmt_time(string time);
297 string field_rec(string fld, string val);
298 static string n3fjp_tstmode();
299 static string n3fjp_opmode();
300 static string n3fjp_opband();
301 static string n3fjp_freq();
302 static void send_control(const string ctl, string val);
303 static void send_action(const string action);
304 static void send_command(const string command, string val="");
305 static void send_data();
306 static void send_data_norig();
307 void get_n3fjp_frequency();
308 void do_n3fjp_add_record_entries();
309 void n3fjp_set_freq(long f);
310 void n3fjp_set_ptt(int on);
311 void n3fjp_add_record(cQsoRec &record);
312 void n3fjp_parse_response(string tempbuff);
313 void n3fjp_rcv_data();
314 static bool connect_to_n3fjp_server();
315 void n3fjp_start();
316 void n3fjp_disconnect(bool clearlog = false);
317 void *n3fjp_loop(void *args);
318 void n3fjp_init(void);
319 void n3fjp_close(void);
320 
321 //======================================================================
322 //
323 //======================================================================
strip(std::string s)324 static std::string strip(std::string s)
325 {
326 	while (s.length() && (s[0] <= ' ')) s.erase(0,1);
327 	while (s.length() && (s[s.length()-1] <= ' ')) s.erase(s.length()-1, 1);
328 	return s;
329 }
330 
331 //======================================================================
332 //
333 //======================================================================
334 
adjust_freq(string sfreq)335 void adjust_freq(string sfreq)
336 {
337 	long freq;
338 	size_t pp = sfreq.find(".");
339 	if (pp == string::npos) return;
340 
341 	while ((sfreq.length() - pp) < 7) sfreq.append("0");
342 	sfreq.erase(pp,1);
343 	freq = atol(sfreq.c_str());
344 
345 	if (freq == 0) return;
346 
347 	wf->rfcarrier(freq);
348 	wf->movetocenter();
349 	show_frequency(freq);
350 
351 	return;
352 
353 	if (progdefaults.N3FJP_sweet_spot) {
354 		int afreq;
355 		if (active_modem->get_mode() == MODE_CW) {
356 			afreq = progdefaults.CWsweetspot;
357 		}
358 		else if (active_modem->get_mode() == MODE_RTTY) {
359 			afreq = progdefaults.RTTYsweetspot;
360 		}
361 		else if (active_modem->get_mode() < MODE_SSB)
362 			afreq = progdefaults.PSKsweetspot;
363 		else {
364 			wf->rfcarrier(freq);
365 			wf->movetocenter();
366 			show_frequency(freq);
367 			return;
368 		}
369 		freq -= (wf->USB() ? afreq : -afreq);
370 		wf->rfcarrier(freq);
371 		wf->movetocenter();
372 		show_frequency(freq);
373 		return;
374 	}
375 	wf->rfcarrier(freq);
376 	wf->movetocenter();
377 	show_frequency(freq);
378 }
379 
380 //======================================================================
381 //
382 //======================================================================
383 static notify_dialog *alert_window = 0;
set_connect_box()384 void set_connect_box()
385 {
386 	if (!alert_window) alert_window = new notify_dialog;
387 	box_n3fjp_connected->color(
388 		n3fjp_connected ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
389 	box_n3fjp_connected->redraw();
390 	if (n3fjp_connected) {
391 		alert_window->notify(_("Connected to N3FJP logger"), 1.0);
392 		REQ(show_notifier, alert_window);
393 		REQ(update_main_title);
394 	}
395 	else {
396 		progdefaults.CONTESTnotes = "";
397 		listbox_contest->index(0);
398 		listbox_QP_contests->index(0);
399 		inp_contest_notes->value(progdefaults.CONTESTnotes.c_str());
400 	}
401 
402 }
403 
n3fjp_print(string s)404 void n3fjp_print(string s)
405 {
406 	if (bEXITING) return;
407 
408 	FILE *n3fjplog = fl_fopen(pathname.c_str(), "a");
409 
410 	time_t t = time(NULL);
411 	struct tm stm;
412 	(void)localtime_r(&t, &stm);
413 	char sztime[12];
414 	memset(sztime, 0, 11);
415 	snprintf(sztime, sizeof(sztime), "[%02d:%02d:%02d] ", stm.tm_hour, stm.tm_min, stm.tm_sec);
416 
417 	s.insert(0, sztime);
418 
419 	if (n3fjplog) {
420 		if (s[s.length()-1]!='\n')
421 			fprintf(n3fjplog, "%s\n", s.c_str());
422 		else
423 			fprintf(n3fjplog, "%s", s.c_str());
424 		fclose(n3fjplog);
425 	}
426 
427 	LOG_VERBOSE("%s", s.c_str());
428 }
429 
n3fjp_show(std::string s)430 void n3fjp_show(std::string s)
431 {
432 	txt_N3FJP_data->insert(s.c_str());
433 	txt_N3FJP_data->redraw();
434 }
435 
n3fjp_disp_report(string s,string fm,bool tofile)436 void n3fjp_disp_report(string s, string fm, bool tofile)
437 {
438 	guard_lock report_lock(&report_mutex);
439 
440 	if (s.empty()) return;
441 
442 	string report = fm.append("\n").append(s);
443 
444 	size_t p;
445 	p = report.find("\r\n");
446 	while (p != string::npos) {
447 		report.replace(p,2,"<crlf>\n");
448 		p = report.find("\r\n");
449 	}
450 	p = report.find("</CMD><CMD>");
451 	while (p != string::npos) {
452 		report.replace(p, 11, "</CMD>\n<CMD>");
453 		p = report.find("</CMD><CMD>");
454 	}
455 
456 	if (progdefaults.enable_N3FJP_log) REQ(n3fjp_show, report);
457 
458 	if (tofile) n3fjp_print(report);
459 
460 }
461 
462 
n3fjp_send(string cmd,bool tofile)463 void n3fjp_send(string cmd, bool tofile)
464 {
465 	guard_lock send_lock(&n3fjp_socket_mutex);
466 	if (!n3fjp_socket) {
467 		n3fjp_print("Socket not present");
468 		return;
469 	}
470 	try {
471 		if (cmd.empty()) return;
472 		n3fjp_disp_report(cmd, "SEND:", tofile);
473 		cmd.append("\r\n");
474 		n3fjp_socket->send(cmd);
475 
476 	} catch (const SocketException& e) {
477 		result.str("");
478 		result << "n3fjp_send()::failed " << e.error() << " " << e.what();
479 		n3fjp_print(result.str());
480 		throw e;
481 	} catch (...) { throw; }
482 }
483 
n3fjp_rcv(string & rx,bool tofile)484 void n3fjp_rcv(string &rx, bool tofile)
485 {
486 	guard_lock read_lock(&n3fjp_socket_mutex);
487 	if (!n3fjp_socket) return;
488 	try {
489 		if (!n3fjp_socket->recv(rx))
490 			rx.clear();
491 		if (rx.empty()) return;
492 		n3fjp_disp_report(rx, "RCVD:", tofile);
493 	} catch (const SocketException& e) {
494 		result.str("");
495 		result << "n3fjp_rcv()::failed " << e.error() << " " << e.what();
496 		n3fjp_print(result.str());
497 		throw e;
498 	} catch (...) { throw; }
499 }
500 
n3fjp_send_freq_mode()501 void n3fjp_send_freq_mode()
502 {
503 	if (!active_modem) return;
504 
505 	string cmd;
506 	char szfreq[20];
507 	double freq = atof(inpFreq->value()) / 1e3;
508 	snprintf(szfreq, sizeof(szfreq), "%f", freq);
509 
510 	if (active_modem->get_mode() != tracked_mode ||
511 		tracked_freq != szfreq) {
512 		tracked_mode = active_modem->get_mode();
513 		tracked_freq = szfreq;
514 		cmd = "<CMD><SENDRIGPOLL><FREQ>";
515 		cmd.append(tracked_freq);
516 		cmd.append("</FREQ><MODE>");
517 		cmd.append( mode_info[tracked_mode].adif_name );
518 		cmd.append("</MODE></CMD>");
519 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
520 	}
521 }
522 
523 //======================================================================
524 //
525 //======================================================================
526 
527 static cQsoRec rec;
528 
n3fjp_sendRSTS(std::string s)529 static void n3fjp_sendRSTS(std::string s)
530 {
531 	if (s.empty()) return;
532 	try {
533 		send_control("RSTS", s);
534 	} catch (...) { throw; }
535 }
536 
n3fjp_sendRSTR(std::string s)537 static void n3fjp_sendRSTR(std::string s)
538 {
539 	if (s.empty()) return;
540 	try {
541 		send_control("RSTR", s);
542 	} catch (...) { throw; }
543 }
544 
send_call(std::string s)545 void send_call(std::string s)
546 {
547 	try {
548 		send_control("CALL", s.c_str());
549 	} catch (const SocketException& e) {
550 		result.str("");
551 		result << "send_call()::failed " << e.error() << " " << e.what();
552 		n3fjp_print(result.str());
553 		throw e;
554 	} catch (...) { throw; }
555 }
556 
send_freq(std::string s)557 void send_freq(std::string s)
558 {
559 	try {
560 		send_control("FREQUENCY", s);
561 	} catch (const SocketException& e) {
562 		result.str("");
563 		result << "send_freq()::failed " << e.error() << " " << e.what();
564 		n3fjp_print(result.str());
565 		throw e;
566 	} catch (...) { throw; }
567 }
568 
send_band(std::string s)569 void send_band(std::string s)
570 {
571 	try {
572 		send_control("BAND", s);
573 	} catch (const SocketException& e) {
574 		result.str("");
575 		result << "send_band()::failed " << e.error() << " " << e.what();
576 		n3fjp_print(result.str());
577 		throw e;
578 	} catch (...) { throw; }
579 }
580 
send_mode(std::string s)581 void send_mode(std::string s)
582 {
583 	try {
584 		send_control("MODE", s);
585 	} catch (const SocketException& e) {
586 		result.str("");
587 		result << "send_mode()::failed " << e.error() << " " << e.what();
588 		n3fjp_print(result.str());
589 		throw e;
590 	} catch (...) { throw; }
591 }
592 
send_state(std::string s)593 void send_state(std::string s)
594 {
595 	try {
596 		send_control("STATE", s);
597 	} catch (const SocketException& e) {
598 		result.str("");
599 		result << "send_state()::failed " << e.error() << " " << e.what();
600 		n3fjp_print(result.str());
601 		throw e;
602 	} catch (...) { throw; }
603 }
604 
send_county(std::string s)605 void send_county(std::string s)
606 {
607 	try {
608 		send_control("COUNTYR", s);
609 	} catch (const SocketException& e) {
610 		result.str("");
611 		result << "send_county()::failed " << e.error() << " " << e.what();
612 		n3fjp_print(result.str());
613 		throw e;
614 	} catch (...) { throw; }
615 }
616 
send_spcnum(std::string s)617 void send_spcnum(std::string s)
618 {
619 	try {
620 		send_control("SPCNUM", s);
621 	} catch (const SocketException& e) {
622 		result.str("");
623 		result << "send_spcnum()::failed " << e.error() << " " << e.what();
624 		n3fjp_print(result.str());
625 		throw e;
626 	} catch (...) { throw; }
627 }
628 
send_name(std::string s)629 void send_name(std::string s)
630 {
631 	try {
632 		send_control("NAMER", s);
633 	} catch (const SocketException& e) {
634 		result.str("");
635 		result << "send_name()::failed " << e.error() << " " << e.what();
636 		n3fjp_print(result.str());
637 		throw e;
638 	} catch (...) { throw; }
639 }
640 
641 //======================================================================
642 //
643 //======================================================================
644 
n3fjp_clear_record()645 void n3fjp_clear_record()
646 {
647 	if(!n3fjp_socket) return;
648 	if (!n3fjp_connected) return;
649 
650 	string cmd = "<CMD><ACTION><VALUE>CLEAR</VALUE></CMD>";
651 	try {
652 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
653 		n3fjp_wait = 100;
654 	} catch (const SocketException& e) {
655 		result.str("");
656 		result << "Error: " << e.error() << ", " << e.what();
657 		n3fjp_print(result.str());
658 	} catch (...) { throw; }
659 }
660 
661 //======================================================================
662 //
663 //======================================================================
664 bool n3fjp_calltab = false;
665 
n3fjp_getfields()666 void n3fjp_getfields()
667 {
668 	string cmd ="<CMD><ALLFIELDSWITHVALUES></CMD>";
669 	try {
670 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
671 		n3fjp_wait = 100;
672 	} catch (const SocketException& e) {
673 		result.str("");
674 		result << "Error: " << e.error() << ", " << e.what();
675 		n3fjp_print(result.str());
676 		n3fjp_calltab = false;
677 	} catch (...) { throw; }
678 }
679 
n3fjp_get_record(string call)680 void n3fjp_get_record(string call)
681 {
682 	if(!n3fjp_socket) return;
683 	if (!n3fjp_connected) return;
684 
685 	if (!n3fjp_calltab) return;
686 
687 	string cmd0, cmd1, cmd2;
688 
689 	cmd0.assign("<CMD><ACTION><VALUE>CLEAR</VALUE></CMD>");
690 
691 	cmd1.assign("<CMD><UPDATE><CONTROL>TXTENTRYCALL</CONTROL><VALUE>");
692 	cmd1.append(call).append("</VALUE></CMD>");
693 
694 	cmd2.assign("<CMD><ACTION><VALUE>CALLTAB</VALUE></CMD>");
695 
696 	try {
697 		n3fjp_send(cmd0, progdefaults.enable_N3FJP_log);
698 		n3fjp_send(cmd1, progdefaults.enable_N3FJP_log);
699 
700 		n3fjp_send(cmd2, progdefaults.enable_N3FJP_log);
701 		n3fjp_calltab = false;
702 
703 		n3fjp_wait = 100;
704 
705 	} catch (const SocketException& e) {
706 		result.str("");
707 		result << "Error: " << e.error() << ", " << e.what();
708 		n3fjp_print(result.str());
709 		n3fjp_calltab = false;
710 	} catch (...) { throw; }
711 }
712 
713 //======================================================================
714 // parse string containing value, e.g.
715 // <FREQ>14.01310</FREQ>
716 //======================================================================
ParseField(string & record,string fieldtag)717 static string ParseField(string &record, string fieldtag)
718 {
719 	string fld_tag_start, fld_tag_end;
720 	fld_tag_start.assign("<").append(fieldtag).append(">");
721 	fld_tag_end.assign("</").append(fieldtag).append(">");
722 	size_t p1 = record.find(fld_tag_start);
723 	if (p1 == string::npos) return "";
724 	p1 += fld_tag_start.length();
725 
726 	size_t p2 = record.find(fld_tag_end, p1);
727 	if (p2 == string::npos) return "";
728 	return record.substr(p1, p2 - p1);
729 }
730 
731 //======================================================================
732 // parse string containing text entry values, e.g.
733 // <CONTROL>TXTENTRYCOUNTYR</CONTROL><VALUE>Saint Louis City</VALUE></CMD>
734 //======================================================================
ParseTextField(string & record,string fieldtag)735 static string ParseTextField(string &record, string fieldtag)
736 {
737 	string fld_tag_start;
738 	fld_tag_start.assign("<CONTROL>TXTENTRY").append(fieldtag).append("</CONTROL>");
739 	size_t p1 = record.find(fld_tag_start);
740 	if (p1 == string::npos) return "";
741 	size_t p2 = record.find("<VALUE>", p1);
742 	if (p2 == string::npos) return "";
743 	p2 += strlen("<VALUE>");
744 	size_t p3 = record.find("</VALUE>", p2);
745 	if (p3 == string::npos) return "";
746 	return record.substr(p2, p3 - p2);
747 }
748 
749 //======================================================================
750 // parse value contents
751 // <VALUE>valuestring</VALUE>
752 //======================================================================
ParseValueField(string field,string & record)753 static string ParseValueField(string field, string &record)
754 {
755 	string start = "<";
756 	start.append(field).append("><VALUE>");
757 	string endvalue = "</VALUE>";
758 	size_t p1 = record.find(start);
759 	size_t p2 = record.find(endvalue, p1);
760 	if ((p1 == string::npos) || (p2 == string::npos) ||
761 		(p2 < p1) ) return "";
762 	p1 += start.length();
763 	return record.substr(p1, p2 - p1);
764 }
765 
ucasestr(string s)766 static string ucasestr(string s)
767 {
768 	for (size_t n = 0; n < s.length(); n++) s[n] = toupper(s[n]);
769 	return s;
770 }
771 
772 //======================================================================
773 //
774 //======================================================================
n3fjp_parse_data_stream(string buffer)775 static void n3fjp_parse_data_stream(string buffer)
776 {
777 	string field;
778 	field = ParseTextField(buffer, "NAMER");
779 	if (!field.empty() && ucasestr(field) != ucasestr(inpName->value())) {
780 		for (size_t n = 1; n < field.length(); n++) field[n] = tolower(field[n]);
781 	}
782 
783 	field = ParseTextField(buffer, "COUNTYR");
784 	if (!field.empty() && field != inpCounty->value() &&
785 		 n3fjp_contest != FJP_NEQP &&
786 		 n3fjp_contest != FJP_7QP)
787 
788 	field = ParseTextField(buffer, "STATE");
789 	if (!field.empty() && field != inpState->value() &&
790 		 n3fjp_contest != FJP_NEQP &&
791 		 n3fjp_contest != FJP_7QP)
792 
793 	field = ParseTextField(buffer, "COUNTRYWORKED");
794 	if (!field.empty() && field != cboCountry->value())
795 
796 	field = ParseTextField(buffer, "GRID");
797 	if (!field.empty() && field != inpLoc->value())
798 
799 	field = ParseTextField(buffer, "FREQUENCY");
800 	if (!field.empty()) adjust_freq(field);
801 
802 	field = ParseTextField(buffer, "CQZONE");
803 	if (!field.empty() && field != inp_CQzone->value())
804 
805 // comments field does not contain \n delimiters
806 // substitute \n for each '-'
807 	field = ParseTextField(buffer, "COMMENTS");
808 	if (!field.empty()) {
809 		size_t p = field.find(" - ");
810 		while (p != string::npos) {
811 			field.replace(p, 3, "\n");
812 			p = field.find(" - ");
813 		}
814 	}
815 }
816 
817 //======================================================================
818 //<CMD><CALLTABEVENT>
819 //  <CALL>ON6NB/P</CALL>
820 //  <BAND>40</BAND>
821 //  <MODE>SSB</MODE>
822 //  <MODETEST>PH</MODETEST>
823 //  <COUNTRY>Belgium</COUNTRY>
824 //</CMD>
825 //======================================================================
n3fjp_parse_calltab_event(string buffer)826 static void n3fjp_parse_calltab_event(string buffer)
827 {
828 //	inpCall->value(ParseField(buffer, "CALL").c_str());
829 	cboCountry->value(ParseField(buffer, "COUNTRY").c_str());
830 	n3fjp_getfields();
831 }
832 
833 //======================================================================
834 //
835 //======================================================================
fmt_date(string date)836 string fmt_date(string date)
837 {
838 	if (date.length() > 6) date.insert(6,"/");
839 	if (date.length() > 4) date.insert(4,"/");
840 	return date;
841 }
842 
fmt_time(string time)843 string fmt_time(string time)
844 {
845 	if (time.length() > 4) time.insert(4,":");
846 	if (time.length() > 2) time.insert(2,":");
847 	return time;
848 }
849 
field_rec(string fld,string val)850 string field_rec(string fld, string val)
851 {
852 	string s;
853 	s.assign("<").append(fld).append(">");
854 	s.append(val);
855 	s.append("</").append(fld).append(">");
856 	return s;
857 }
858 
n3fjp_tstmode()859 static string n3fjp_tstmode()
860 {
861 	if (!active_modem)
862 		return "PH";
863 
864 	if (active_modem->get_mode() == MODE_CW)
865 		return "CW";
866 
867 	if (active_modem->get_mode() == MODE_SSB)
868 		return "PH";
869 
870 	if (active_modem->get_mode() < MODE_SSB)
871 		return mode_info[active_modem->get_mode()].adif_name;
872 
873 	return "";
874 }
875 
n3fjp_opmode()876 static string n3fjp_opmode()
877 {
878 	if (!active_modem)
879 		return "PH";
880 
881 	if (active_modem->get_mode() == MODE_CW)
882 		return "CW";
883 
884 	if (active_modem->get_mode() == MODE_SSB)
885 		return "PH";
886 
887 	if (active_modem->get_mode() < MODE_SSB)
888 		return mode_info[active_modem->get_mode()].adif_name;
889 
890 	return "";
891 }
892 
n3fjp_opband()893 static string n3fjp_opband()
894 {
895 	if (!active_modem) return "";
896 
897 	float freq = qsoFreqDisp->value();
898 	freq /= 1e6;
899 
900 	if (freq >= 1.8 && freq < 3.5) return "160";
901 	if (freq >= 3.5 && freq < 5.3) return "80";
902 	if (freq >= 5.3 && freq < 5.5) return "60";
903 	if (freq >= 7.0 && freq < 7.5) return "40";
904 	if (freq >= 14.0 && freq < 18.0) return "20";
905 	if (freq >= 18.0 && freq < 21.0) return "17";
906 	if (freq >= 21.0 && freq < 24.0) return "15";
907 	if (freq >= 24.0 && freq < 28.0) return "12";
908 	if (freq >= 28.0 && freq < 50.0) return "10";
909 	if (freq >= 50.0 && freq < 70.0) return "6";
910 	if (freq >= 144.0 && freq < 222.0) return "2";
911 	if (freq >= 222.0 && freq < 420.0) return "222";
912 	if (freq >= 420.0 && freq < 444.0) return "440";
913 	return "";
914 }
915 
n3fjp_freq()916 static string n3fjp_freq()
917 {
918 	if (!active_modem) return "";
919 	float freq = qsoFreqDisp->value();
920 	if (progdefaults.N3FJP_modem_carrier) {
921 		if (ModeIsLSB(mode_info[active_modem->get_mode()].adif_name)) {
922 			freq -= active_modem->get_txfreq();
923 			if (active_modem->get_mode() == MODE_RTTY)
924 				freq -= progdefaults.rtty_shift / 2;
925 		} else {
926 			freq += active_modem->get_txfreq();
927 			if (active_modem->get_mode() == MODE_RTTY)
928 				freq += progdefaults.rtty_shift / 2;
929 		}
930 	}
931 	freq /= 1e6;
932 	char szfreq[20];
933 	snprintf(szfreq, sizeof(szfreq), "%f", freq);
934 	return szfreq;
935 }
936 
send_control(const string ctl,string val)937 static void send_control(const string ctl, string val)
938 {
939 	string cmd;
940 	cmd.assign("<CMD><UPDATE><CONTROL>TXTENTRY").append(ctl);
941 	cmd.append("</CONTROL><VALUE>");
942 	cmd.append(val);
943 	cmd.append("</VALUE></CMD>");
944 	try {
945 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
946 		n3fjp_wait = 100;
947 	} catch (...) { throw; }
948 }
949 
send_action(const string action)950 static void send_action(const string action)
951 {
952 	string cmd;
953 	cmd.assign("<CMD><ACTION><VALUE>");
954 	cmd.append(action);
955 	cmd.append("</VALUE></CMD>");
956 	try {
957 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
958 		n3fjp_wait = 200;//100;
959 	} catch (...) { throw; }
960 }
961 
send_command(const string command,string val)962 static void send_command(const string command, string val)
963 {
964 	string cmd;
965 	cmd.assign("<CMD><").append(command).append(">");
966 	if (!val.empty())
967 		cmd.append("<VALUE>").append(val).append("</VALUE>");
968 	cmd.append("</CMD>");
969 	try {
970 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
971 //		MilliSleep(5);
972 		n3fjp_wait = 100;
973 	} catch (...) { throw; }
974 }
975 
n3fjp_send_NONE()976 static void n3fjp_send_NONE()
977 {
978 	try {
979 		send_control("DATE", fmt_date(rec.getField(QSO_DATE)));
980 		send_control("TIMEON", fmt_time(rec.getField(TIME_ON)));
981 		send_control("TIMEOFF", fmt_time(rec.getField(TIME_OFF)));
982 
983 		send_name(strip(rec.getField(NAME)));
984 		send_control("COMMENTS", strip(rec.getField(NOTES)));
985 		send_control("POWER", strip(rec.getField(TX_PWR)));
986 		send_state(rec.getField(STATE));
987 		send_control("GRID", strip(rec.getField(GRIDSQUARE)));
988 		send_control("QTHGROUP", strip(rec.getField(QTH)));
989 		send_county(rec.getField(CNTY));
990 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
991 	} catch (...) { throw; }
992 }
993 
994 // ARRL Field Day
n3fjp_send_FD()995 static void n3fjp_send_FD()
996 {
997 	try {
998 		send_control("MODETST", n3fjp_tstmode());
999 		send_control("CLASS", strip(ucasestr(rec.getField(CLASS))));
1000 		send_control("SECTION", strip(ucasestr(rec.getField(ARRL_SECT))));
1001 	} catch (...) { throw; }
1002 }
1003 
1004 // Winter Field Day
n3fjp_send_WFD()1005 static void n3fjp_send_WFD()
1006 {
1007 	try {
1008 		send_control("MODETST", n3fjp_tstmode());
1009 		send_control("CLASS", strip(ucasestr(rec.getField(CLASS))));
1010 		send_control("SECTION", strip(ucasestr(rec.getField(ARRL_SECT))));
1011 	} catch (...) { throw; }
1012 }
1013 
1014 // Kids Day
n3fjp_send_KD()1015 static void n3fjp_send_KD()
1016 {
1017 	try {
1018 		send_name(rec.getField(NAME));
1019 
1020 		send_control("AGE", rec.getField(AGE));
1021 
1022 		std::string stprc = strip(ucasestr(rec.getField(STATE)));
1023 		if (stprc.empty())
1024 			stprc = strip(ucasestr(rec.getField(VE_PROV)));
1025 		if (stprc.empty())
1026 			stprc = strip(rec.getField(COUNTRY));
1027 		send_spcnum(stprc);
1028 
1029 		send_control("COMMENTS", strip(rec.getField(XCHG1)));
1030 	} catch (...) { throw; }
1031 }
1032 
1033 // ARRL Rookie Roundup
n3fjp_send_ARR()1034 static void n3fjp_send_ARR()
1035 {
1036 	try {
1037 		send_name(rec.getField(NAME));
1038 		send_control("CHECK", rec.getField(CHECK));
1039 		if (rec.getField(XCHG1)[0])
1040 			send_spcnum(strip(ucasestr(rec.getField(XCHG1))));
1041 		else
1042 			send_spcnum(strip(ucasestr(rec.getField(COUNTRY))));
1043 	} catch (...) { throw; }
1044 }
1045 
1046 // ARRL RTTY
n3fjp_send_RTTY()1047 static void n3fjp_send_RTTY()
1048 {
1049 	try {
1050 		if (rec.getField(SRX)[0])
1051 			send_spcnum(strip(rec.getField(SRX)));
1052 		else if (rec.getField(STATE)[0])
1053 			send_spcnum(strip(ucasestr(rec.getField(STATE))));
1054 		else if (rec.getField(VE_PROV)[0])
1055 			send_spcnum(strip(ucasestr(rec.getField(VE_PROV))));
1056 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1057 	} catch (...) { throw; }
1058 }
1059 
1060 // ARRL School Club Roundup
n3fjp_send_ASCR()1061 static void n3fjp_send_ASCR()
1062 {
1063 	try {
1064 		send_name(strip(rec.getField(NAME)));
1065 		send_control("CLASS", ucasestr(rec.getField(CLASS)));
1066 		send_spcnum(ucasestr(rec.getField(XCHG1)));
1067 	} catch (...) { throw; }
1068 }
1069 
1070 // ARRL Jamboree On The Air
n3fjp_send_JOTA()1071 static void n3fjp_send_JOTA()
1072 {
1073 	try {
1074 		send_name(rec.getField(SCOUTR));	// received scout name
1075 		send_control("NAMES", rec.getField(SCOUTS));	// sent scout name
1076 		send_control("CHECK", rec.getField(TROOPR));	// received troop number
1077 		send_control("TROOPS", rec.getField(TROOPS));	// sent troop number
1078 		if (state_test(rec.getField(STATE)))
1079 			send_spcnum(ucasestr(rec.getField(STATE)));
1080 		else if (province_test(rec.getField(VE_PROV)))
1081 			send_spcnum(ucasestr(rec.getField(VE_PROV)));
1082 		else
1083 			send_spcnum(rec.getField(COUNTRY));	// St / Pr / Cntry
1084 		send_control("COMMENTS", rec.getField(NOTES));
1085 	} catch (...) { throw; }
1086 }
1087 
1088 // CQ WPX
n3fjp_send_WPX()1089 static void n3fjp_send_WPX()
1090 {
1091 	try {
1092 		send_call(rec.getField(CALL));
1093 		send_freq(n3fjp_freq());
1094 		send_band(n3fjp_opband());
1095 		send_mode(n3fjp_opmode());
1096 
1097 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1098 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1099 	} catch (...) { throw; }
1100 }
1101 
1102 // Italian ARI International DX
n3fjp_send_IARI()1103 static void n3fjp_send_IARI()
1104 {
1105 	try {
1106 		if (rec.getField(SRX)[0])
1107 			send_spcnum(rec.getField(SRX));
1108 		else
1109 			send_spcnum(ucasestr(rec.getField(XCHG1)));
1110 //		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1111 	} catch (...) { throw; }
1112 }
1113 
1114 // North American Sprint
n3fjp_send_NAS()1115 static void n3fjp_send_NAS()
1116 {
1117 	try {
1118 		send_name(rec.getField(NAME));
1119 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1120 		send_spcnum(strip(ucasestr(rec.getField(XCHG1))));
1121 	} catch (...) { throw; }
1122 }
1123 
1124 // CQ World Wide RTTY
n3fjp_send_CQWWRTTY()1125 static void n3fjp_send_CQWWRTTY()
1126 {
1127 	try {
1128 		send_control("CQZONE", strip(rec.getField(CQZ)));
1129 		send_state(rec.getField(STATE));
1130 		send_control("COUNTRYWORKED", strip(rec.getField(COUNTRY)));
1131 	} catch (...) { throw; }
1132 }
1133 
1134 // CQ World Wide DX
n3fjp_send_CQWWDX()1135 static void n3fjp_send_CQWWDX()
1136 {
1137 	try {
1138 		send_control("CQZONE", strip(rec.getField(CQZ)));
1139 		send_control("COUNTRYWORKED", strip(rec.getField(COUNTRY)));
1140 	} catch (...) { throw; }
1141 }
1142 
1143 // Sweepstakes
n3fjp_send_SS()1144 static void n3fjp_send_SS()
1145 {
1146 	try {
1147 		send_control("SERIALNOR", strip(rec.getField(SS_SERNO)));
1148 		send_control("PRECEDENCE", strip(rec.getField(SS_PREC)));
1149 		send_control("CHECK", strip(rec.getField(SS_CHK)));
1150 		send_control("SECTION", strip(rec.getField(SS_SEC)));
1151 	} catch (...) { throw; }
1152 }
1153 
1154 // North American QSO Party
n3fjp_send_NAQP()1155 static void n3fjp_send_NAQP()
1156 {
1157 	try {
1158 		send_name(rec.getField(NAME));
1159 		if (strlen(rec.getField(XCHG1)) > 0)
1160 			send_spcnum(ucasestr(rec.getField(XCHG1)));
1161 	} catch (...) { throw; }
1162 }
1163 
1164 // Ten Ten
n3fjp_send_1010()1165 static void n3fjp_send_1010()
1166 {
1167 	try {
1168 		send_name(rec.getField(NAME));
1169 		send_control("1010", rec.getField(TEN_TEN));
1170 		if (strlen(rec.getField(XCHG1)) > 0)
1171 			send_spcnum(ucasestr(rec.getField(XCHG1)));
1172 	} catch (...) { throw; }
1173 }
1174 
1175 // Africa International DX
n3fjp_send_AIDX()1176 static void n3fjp_send_AIDX()
1177 {
1178 	try {
1179 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1180 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1181 	} catch (...) { throw; }
1182 }
1183 
1184 // ARRL International DX (CW)
n3fjp_send_AICW()1185 static void n3fjp_send_AICW()
1186 {
1187 	try {
1188 		send_spcnum(rec.getField(XCHG1));
1189 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1190 	} catch (...) { throw; }
1191 }
1192 
n3fjp_send_GENERIC()1193 static void n3fjp_send_GENERIC()
1194 {
1195 	try {
1196 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1197 		send_spcnum(strip(ucasestr(rec.getField(XCHG1))));
1198 	} catch (...) { throw; }
1199 }
1200 
n3fjp_send_VHF()1201 static void n3fjp_send_VHF()
1202 {
1203 	try {
1204 		std::string grid = strip(rec.getField(GRIDSQUARE));
1205 		if (grid.length() > 4) grid.erase(4);
1206 		send_control("GRID", grid);
1207 	} catch (...) { throw; }
1208 }
1209 
n3fjp_send_WAE()1210 static void n3fjp_send_WAE()
1211 {
1212 	try {
1213 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1214 		send_control("COUNTRYWORKED", rec.getField(COUNTRY));
1215 	} catch (...) { throw; }
1216 }
1217 
1218 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1219 // "MD SQP"    "",    "B",  "B",    "",    "",     "",     "B",   ""
1220 
n3fjp_send_MDQSP()1221 static void n3fjp_send_MDQSP()
1222 {
1223 	try {
1224 
1225 		if (rec.getField(XCHG1)[0])
1226 			send_control("CATEGORY", ucasestr(strip(rec.getField(XCHG1))));
1227 		send_county(strip(rec.getField(CNTY)));
1228 
1229 		if (n3fjp_in_state) {
1230 			send_county(ucasestr(strip(rec.getField(CNTY))));
1231 			if (rec.getField(STATE)[0])
1232 				send_spcnum(rec.getField(STATE));
1233 			else if (rec.getField(VE_PROV)[0])
1234 				send_spcnum(rec.getField(VE_PROV));
1235 			else if (rec.getField(COUNTRY)[0])
1236 				send_spcnum(rec.getField(COUNTRY));
1237 		}
1238 	} catch (...) { throw; }
1239 }
1240 
1241 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1242 // "New Eng",  "B",   "B",  "B",    "",    "I",    "",     "",    "CCCSS"
1243 
1244 // MA, NH, VT, MA, CT, RI
n3fjp_send_NEQP()1245 static void n3fjp_send_NEQP()
1246 {
1247 	try {
1248 		if (rec.getField(CNTY)[0])
1249 			send_county(rec.getField(CNTY));
1250 
1251 		if (rec.getField(STATE)[0])
1252 			send_spcnum(rec.getField(STATE));
1253 		else if (rec.getField(COUNTRY)[0])
1254 			send_spcnum(rec.getField(COUNTRY));
1255 	} catch (...) { throw; }
1256 }
1257 
1258 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1259 // "7QP",      "B",   "B",  "B",    "",    "I",    "",     "",    "SSCCC"
1260 // AZ ID OR MT NV WA WY
n3fjp_send_7QP()1261 static void n3fjp_send_7QP()
1262 {
1263 	static string st7QP = "AZ ID OR MT NV WA WY UT";
1264 	try {
1265 		std::string st = rec.getField(STATE);
1266 		std::string cnty = rec.getField(CNTY);
1267 
1268 		if (cnty.length() == 5) {
1269 			if (st7QP.find(cnty.substr(0,2)) != std::string::npos)
1270 				st = cnty.substr(0,2);
1271 		}
1272 
1273 		send_spcnum(st);
1274 		send_county(cnty);
1275 
1276 		if (progdefaults.SQSOinstate && !st.empty()) { // in region
1277 			if (st7QP.find(st) != std::string::npos) {
1278 				if (!cnty.empty()) send_county(cnty);
1279 				send_spcnum(st);
1280 			} else
1281 				send_spcnum(st);
1282 		} else {  // out of region
1283 			if (!st.empty() && st7QP.find(st) != std::string::npos) {
1284 				if (!cnty.empty()) send_county(cnty);
1285 				send_spcnum(st);
1286 			}
1287 		}
1288 
1289 	} catch (...) { throw; }
1290 }
1291 
1292 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1293 // "QSOP 1"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
1294 //AL, AR, FL, GA, HI, IA, ID, IL, KS, KY, LA, MO, MI, ND, NJ, BC, 7QP
n3fjp_send_QP1()1295 static void n3fjp_send_QP1()
1296 {
1297 	try {
1298 		std::string st = rec.getField(STATE);
1299 		if (st.empty()) st = QSOparties.qso_parties[progdefaults.SQSOcontest].state;
1300 		if (rec.getField(STATE)[0])
1301 			send_spcnum(rec.getField(STATE));
1302 		else if (rec.getField(VE_PROV)[0])
1303 			send_spcnum(rec.getField(VE_PROV));
1304 		else if (rec.getField(COUNTRY)[0])
1305 			send_spcnum(rec.getField(COUNTRY));
1306 		if (rec.getField(CNTY)[0]) {
1307 			if (st == "BC") { // British Columbia
1308 				Cstates cs;
1309 				send_county(cs.cnty_short(st, rec.getField(CNTY)));
1310 			} else
1311 				send_county(rec.getField(CNTY));
1312 		}
1313 	} catch (...) { throw; }
1314 }
1315 
1316 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1317 // "QSOP 2"    "B",   "B",  "B",    "",    "B",    "",     "",    ""
1318 
1319 // MT, NE, NY, OH, OK, ON, SK, TN, TX, VT, WA, ME
n3fjp_send_QP2()1320 static void n3fjp_send_QP2()
1321 {
1322 	try {
1323 		if (rec.getField(STATE)[0])
1324 			send_spcnum(rec.getField(STATE));
1325 		else if (rec.getField(VE_PROV)[0])
1326 			send_spcnum(rec.getField(VE_PROV));
1327 		else if (rec.getField(COUNTRY)[0])
1328 			send_spcnum(rec.getField(COUNTRY));
1329 		if (rec.getField(CNTY)[0])
1330 			send_county(rec.getField(CNTY));
1331 	} catch (...) { throw; }
1332 }
1333 
1334 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1335 // "QSOP 3"    "",    "B",  "B",    "B",   "I",    "",     "",    ""
1336 
1337 // AZ, CA, MI, PA, VI
n3fjp_send_QP3()1338 static void n3fjp_send_QP3()
1339 {
1340 	try {
1341 		if (n3fjp_in_state) {  // in state log
1342 			if (rec.getField(STATE)[0]) {
1343 				send_spcnum(rec.getField(STATE));
1344 //				string county = states.county(rec.getField(STATE), rec.getField(CNTY));
1345 //				if (!county.empty())
1346 				if (rec.getField(CNTY)[0])
1347 					send_county(rec.getField(CNTY));
1348 			}
1349 			else if (rec.getField(VE_PROV)[0])
1350 				send_spcnum(rec.getField(VE_PROV));
1351 			else if (rec.getField(COUNTRY)[0])
1352 				send_spcnum(rec.getField(COUNTRY));
1353 		} else { // out of state log
1354 			std::string county = rec.getField(CNTY);
1355 			if (!county.empty())
1356 //			{
1357 				send_county(county);
1358 //			} else
1359 //				send_county(rec.getField(CNTY)); // may be ARRL section
1360 		}
1361 		send_control("SERIALNOR", strip(rec.getField(SRX)));
1362 	} catch (...) { throw; }
1363 }
1364 
1365 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1366 // "QSOP 4"    "",    "B",  "B",    "",    "I",    "B",    "",    ""
1367 
1368 // CO, MN, NM
n3fjp_send_QP4()1369 static void n3fjp_send_QP4()
1370 {
1371 	try {
1372 // RST sent/rcvd not required, but will be accepted by logger
1373 		if (n3fjp_in_state) {
1374 			if (rec.getField(STATE)[0])
1375 				send_spcnum(rec.getField(STATE));
1376 			else if (rec.getField(VE_PROV)[0])
1377 				send_spcnum(rec.getField(VE_PROV));
1378 			else if (rec.getField(COUNTRY)[0])
1379 				send_spcnum(rec.getField(COUNTRY));
1380 			if (rec.getField(NAME)[0])
1381 				send_name(rec.getField(NAME));
1382 			if (rec.getField(CNTY)[0])
1383 				send_county(rec.getField(CNTY));
1384 		} else {
1385 //			std::string st = QSOparties.qso_parties[progdefaults.SQSOcontest].state;
1386 //			send_county(states.county(st, rec.getField(CNTY)));
1387 			send_county(rec.getField(CNTY));
1388 			if (rec.getField(NAME)[0])
1389 				send_name(rec.getField(NAME));
1390 		}
1391 	} catch (...) { throw; }
1392 }
1393 
1394 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1395 // "QSOP 5"    "B",   "I",  "B",    "",    "",     "",     "",    ""
1396 
1397 // SC, WV
n3fjp_send_QP5()1398 static void n3fjp_send_QP5()
1399 {
1400 	try {
1401 		if (n3fjp_in_state) {
1402 			if (rec.getField(STATE)[0])
1403 				send_spcnum(rec.getField(STATE));
1404 			else if (rec.getField(VE_PROV)[0])
1405 				send_spcnum(rec.getField(VE_PROV));
1406 			else if (rec.getField(COUNTRY)[0])
1407 				send_spcnum(rec.getField(COUNTRY));
1408 			send_county(rec.getField(CNTY));
1409 		} else {
1410 			send_county(rec.getField(CNTY));
1411 		}
1412 	} catch (...) { throw; }
1413 }
1414 
1415 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1416 // "QSOP 6"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
1417 
1418 // NC, WI
n3fjp_send_QP6()1419 static void n3fjp_send_QP6()
1420 {
1421 	try {
1422 		if (n3fjp_in_state) {
1423 			if (rec.getField(STATE)[0])
1424 				send_spcnum(rec.getField(STATE));
1425 			else if (rec.getField(VE_PROV)[0])
1426 				send_spcnum(rec.getField(VE_PROV));
1427 			else if (rec.getField(COUNTRY)[0])
1428 				send_spcnum(rec.getField(COUNTRY));
1429 			if (rec.getField(CNTY)[0])
1430 				send_county(rec.getField(CNTY));
1431 		} else {
1432 //			send_county(states.county(rec.getField(STATE), rec.getField(CNTY)));
1433 			send_county(rec.getField(CNTY));
1434 		}
1435 	} catch (...) { throw; }
1436 }
1437 
1438 // check fields for duplicates
1439 
1440 // ARRL Field Day
n3fjp_check_FD()1441 static void n3fjp_check_FD()
1442 {
1443 	try {
1444 		send_control("MODETST", n3fjp_tstmode());
1445 		send_control("CLASS", inpClass->value());
1446 		send_control("SECTION", inpSection->value());
1447 	} catch (...) { throw; }
1448 	return;
1449 }
1450 
1451 // Winter Field Day
n3fjp_check_WFD()1452 static void n3fjp_check_WFD()
1453 {
1454 	try {
1455 		send_control("MODETST", n3fjp_tstmode());
1456 		send_control("CLASS", inpClass->value());
1457 		send_control("SECTION", inpSection->value());
1458 	} catch (...) { throw; }
1459 	return;
1460 }
1461 
1462 // Kids Day
n3fjp_check_KD()1463 static void n3fjp_check_KD()
1464 {
1465 	try {
1466 		send_name(inpName->value());
1467 		send_control("AGE", inp_KD_age->value());
1468 		send_spcnum(ucasestr(inpQTH->value()));
1469 		send_control("COMMENTS", inpXchgIn->value());
1470 	} catch (...) { throw; }
1471 	return;
1472 }
1473 
1474 // ARRL Rookie Roundup
n3fjp_check_ARR()1475 static void n3fjp_check_ARR()
1476 {
1477 	try {
1478 		send_name(inpName->value());
1479 		send_control("CHECK", inp_ARR_check->value());
1480 		if (inpXchgIn->value()[0])
1481 			send_spcnum(ucasestr(inpXchgIn->value()));
1482 		else
1483 			send_spcnum(ucasestr(cboCountry->value()));
1484 	} catch (...) { throw; }
1485 	return;
1486 }
1487 
1488 // ARRL RTTY
n3fjp_check_RTTY()1489 static void n3fjp_check_RTTY()
1490 {
1491 	try {
1492 		if (inpSerNo->value()[0])
1493 			send_spcnum(inpSerNo->value());
1494 		else if (inpState->value()[0])
1495 			send_spcnum(ucasestr(inpState->value()));
1496 		else if (inpVEprov->value()[0])
1497 			send_spcnum(ucasestr(ucasestr(inpVEprov->value())));
1498 		send_control("COUNTRYWORKED", cboCountry->value());
1499 	} catch (...) { throw; }
1500 	return;
1501 }
1502 
1503 // ARRL School Club Roundup
n3fjp_check_ASCR()1504 static void n3fjp_check_ASCR()
1505 {
1506 	try {
1507 		send_name(inpName->value());
1508 		send_spcnum(inpXchgIn->value());
1509 		send_control("CLASS", ucasestr(inpClass->value()));
1510 	} catch (...) { throw; }
1511 	return;
1512 }
1513 
1514 // ARRL Jamboree On The Air
n3fjp_check_JOTA()1515 static void n3fjp_check_JOTA()
1516 {
1517 	try {
1518 		send_name(inpName->value());
1519 
1520 		send_control("CHECK", rec.getField(TROOPR));	// received troop number
1521 		send_control("TROOPS", inp_JOTA_troop->value());	// sent troop number
1522 		if (state_test(inpState->value()))
1523 			send_spcnum(inpState->value());
1524 		else if (province_test(inpVEprov->value()))
1525 			send_spcnum(inpVEprov->value());
1526 		else
1527 			send_spcnum(ucasestr(cboCountry->value()));	// St / Pr / Cntry
1528 		send_control("CHECK", inp_JOTA_troop->value());
1529 	} catch (...) { throw; }
1530 	return;
1531 }
1532 
1533 // CQ WPX
n3fjp_check_WPX()1534 static void n3fjp_check_WPX()
1535 {
1536 	try {
1537 		send_control("COUNTRYWORKED", cboCountry->value());
1538 		send_control("SERIALNOR", inpSerNo->value());
1539 //		send_control("CHECK", inpXchgIn->value());
1540 	} catch (...) { throw; }
1541 	return;
1542 }
1543 
1544 // Italian ARI International DX
n3fjp_check_IARI()1545 static void n3fjp_check_IARI()
1546 {
1547 	try {
1548 		if (inpSerNo->value()[0])
1549 			send_spcnum(inpSerNo->value());
1550 		else
1551 			send_spcnum(inpXchgIn->value());
1552 //		send_control("COUNTRYWORKED", cboCountry->value());
1553 	} catch (...) { throw; }
1554 	return;
1555 }
1556 
1557 // North American Sprint
n3fjp_check_NAS()1558 static void n3fjp_check_NAS()
1559 {
1560 	try {
1561 		send_name(inpName->value());
1562 		send_control("SERIALNOR", inpSerNo->value());
1563 		send_spcnum(inpXchgIn->value());
1564 	} catch (...) { throw; }
1565 	return;
1566 }
1567 
1568 // CQ World Wide RTTY
n3fjp_check_CQWWRTTY()1569 static void n3fjp_check_CQWWRTTY()
1570 {
1571 	try {
1572 		send_control("CQZONE", inp_CQzone->value());
1573 		send_state(inpState->value());
1574 		send_control("COUNTRYWORKED", cboCountry->value());
1575 	} catch (...) { throw; }
1576 	return;
1577 }
1578 
1579 // CQ World Wide DX
n3fjp_check_CQWWDX()1580 static void n3fjp_check_CQWWDX()
1581 {
1582 	try {
1583 		send_control("CQZONE", inp_CQzone->value());
1584 		send_control("COUNTRYWORKED", cboCountry->value());
1585 	} catch (...) { throw; }
1586 	return;
1587 }
1588 
1589 // Sweepstakes
n3fjp_check_SS()1590 static void n3fjp_check_SS()
1591 {
1592 	try {
1593 		send_control("SERIALNOR", inpSerNo->value());
1594 		send_control("PRECEDENCE", inp_SS_Precedence->value());
1595 		send_control("CHECK", inp_SS_Check->value());
1596 		send_control("SECTION", inp_SS_Section->value());
1597 	} catch (...) { throw; }
1598 	return;
1599 }
1600 
1601 // North American QSO Party
n3fjp_check_NAQP()1602 static void n3fjp_check_NAQP()
1603 {
1604 	try {
1605 		send_name(inpName->value());
1606 		send_spcnum(inpXchgIn->value());
1607 	} catch (...) { throw; }
1608 	return;
1609 }
1610 
1611 // Ten Ten
n3fjp_check_1010()1612 static void n3fjp_check_1010()
1613 {
1614 	try {
1615 		send_name(inpName->value());
1616 		send_control("1010", inp_1010_nr->value());
1617 		send_spcnum(inpXchgIn->value());
1618 	} catch (...) { throw; }
1619 	return;
1620 }
1621 
1622 // Africa International DX
n3fjp_check_AIDX()1623 static void n3fjp_check_AIDX()
1624 {
1625 	try {
1626 		send_control("SERIALNOR", inpSerNo->value());
1627 		send_control("COUNTRYWORKED", cboCountry->value());
1628 	} catch (...) { throw; }
1629 	return;
1630 }
1631 
1632 // ARRL International DX (CW)
n3fjp_check_AICW()1633 static void n3fjp_check_AICW()
1634 {
1635 	try {
1636 		send_spcnum(inpSPCnum->value());
1637 		send_control("COUNTRYWORKED", cboCountry->value());
1638 	} catch (...) { throw; }
1639 	return;
1640 }
1641 
n3fjp_check_GENERIC()1642 static void n3fjp_check_GENERIC()
1643 {
1644 	try {
1645 		send_control("SERIALNOR", inpSerNo->value());
1646 		send_spcnum(ucasestr(inpXchgIn->value()));
1647 	} catch (...) { throw; }
1648 	return;
1649 }
1650 
n3fjp_check_VHF()1651 static void n3fjp_check_VHF()
1652 {
1653 	try {
1654 		std::string grid = inpLoc->value();
1655 		if (grid.length() > 4) grid.erase(4);
1656 		send_control("GRID", grid);
1657 	} catch (...) { throw; }
1658 	return;
1659 }
1660 
n3fjp_check_WAE()1661 static void n3fjp_check_WAE()
1662 {
1663 	try {
1664 		send_control("SERIALNOR", inpSerNo->value());
1665 		send_control("COUNTRYWORKED", cboCountry->value());
1666 	} catch (...) { throw; }
1667 	return;
1668 }
1669 
1670 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1671 // "MD SQP"    "",    "B",  "B",    "",    "",     "",     "B",   ""
1672 
n3fjp_check_MDQSP()1673 static void n3fjp_check_MDQSP()
1674 {
1675 	try {
1676 		send_county(inpCounty->value());
1677 
1678 		if (inpSQSO_category->value()[0])
1679 			send_control("CATEGORY", inpSQSO_category->value());
1680 
1681 		if (n3fjp_in_state) {
1682 			if (inpState->value()[0])
1683 				send_spcnum(inpState->value());
1684 			else if (inpVEprov->value()[0])
1685 				send_spcnum(inpVEprov->value());
1686 			else if (cboCountry->value()[0])
1687 				send_spcnum(cboCountry->value());
1688 		}
1689 
1690 	} catch (...) { throw; }
1691 	return;
1692 }
1693 
1694 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1695 // "New Eng",  "B",   "B",  "B",    "",    "I",    "",     "",    "CCCSS"
1696 
1697 // MA, NH, VT, MA, CT, RI
n3fjp_check_NEQP()1698 static void n3fjp_check_NEQP()
1699 {
1700 	static std::string stNEQP = "CT MA ME NH RI VT";
1701 	try {
1702 		std::string st = inpState->value();
1703 		std::string cnty = inpCounty->value();
1704 
1705 		if (cnty.length() == 5) {
1706 			if (stNEQP.find(cnty.substr(cnty.length() - 3, 2)) != std::string::npos)
1707 				st = cnty.substr(cnty.length() - 3, 2);
1708 		}
1709 
1710 		if (progdefaults.SQSOinstate && !st.empty()) {  // in region
1711 			if (stNEQP.find(st) != std::string::npos) {
1712 				if(!cnty.empty()) send_county(cnty);
1713 				send_spcnum(st);
1714 			} else
1715 				send_spcnum(st);
1716 		} else {  // out of region
1717 			if (!st.empty() && stNEQP.find(st) != std::string::npos) {
1718 				if (!cnty.empty()) send_county(cnty);
1719 				send_spcnum(st);
1720 			}
1721 		}
1722 	} catch (...) { throw; }
1723 
1724 	return;
1725 }
1726 
1727 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1728 // "7QP",      "B",   "B",  "B",    "",    "I",    "",     "",    "SSCCC"
1729 // AZ ID OR MT NV WA WY
n3fjp_check_7QP()1730 static void n3fjp_check_7QP()
1731 {
1732 	try {
1733 		static string st7QP = "AZ ID OR MT NV WA WY UT";
1734 		std::string st = inpState->value();
1735 		std::string cnty = inpCounty->value();
1736 
1737 		if (cnty.length() == 5 && !inpState->value()[0]) {
1738 			if (st7QP.find(cnty.substr(0,2)) != std::string::npos)
1739 				st = cnty.substr(0,2);
1740 		}
1741 
1742 		if (progdefaults.SQSOinstate && !st.empty()) { // in region
1743 			if (st7QP.find(st) != std::string::npos) {
1744 				if (!cnty.empty()) send_county(cnty);
1745 				send_spcnum(st);
1746 			} else
1747 				send_spcnum(st);
1748 		} else {  // out of region
1749 			if (!st.empty() && st7QP.find(st) != std::string::npos) {
1750 				if (!cnty.empty()) send_county(cnty);
1751 				send_spcnum(st);
1752 			}
1753 		}
1754 
1755 	} catch (...) { throw; }
1756 
1757 	return;
1758 }
1759 
1760 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1761 // "QSOP 1"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
1762 //AL, AR, FL, GA, HI, IA, ID, IL, IN, KS, KY, LA, MO, MI, ND, NJ, BC, 7QP
n3fjp_check_QP1()1763 static void n3fjp_check_QP1()
1764 {
1765 	try {
1766 		std::string st = inpState->value();
1767 		std::string cntry = cboCountry->value();
1768 		if (st.empty())
1769 			st = QSOparties.qso_parties[progdefaults.SQSOcontest].state;
1770 
1771 		if (inpState->value()[0])
1772 			send_spcnum(inpState->value());
1773 		if (inpVEprov->value()[0])
1774 			send_spcnum(inpVEprov->value());
1775 		if (!cntry.empty() && cntry != "USA")
1776 			send_spcnum(cboCountry->value());
1777 
1778 		if (inpCounty->value()[0]) {
1779 			if (st == "BC" ) { // British Columbia
1780 				Cstates cs;
1781 				send_county(cs.cnty_short("BC", inpCounty->value()));
1782 			} else
1783 				send_county(inpCounty->value());
1784 		}
1785 	} catch (...) { throw; }
1786 
1787 	return;
1788 }
1789 
1790 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1791 // "QSOP 2"    "B",   "B",  "B",    "",    "B",    "",     "",    ""
1792 
1793 // MT, NE, NY, OH, OK, ON, SK, TN, TX, VT, WA, ME
n3fjp_check_QP2()1794 static void n3fjp_check_QP2()
1795 {
1796 	try {
1797 		if (inpState->value()[0])
1798 			send_spcnum(inpState->value());
1799 		else if (inpVEprov->value()[0])
1800 			send_spcnum(inpVEprov->value());
1801 		else if (cboCountry->value()[0])
1802 			send_spcnum(cboCountry->value());
1803 		if (inpCounty->value()[0])
1804 			send_county(inpCounty->value());
1805 	} catch (...) { throw; }
1806 
1807 	return;
1808 }
1809 
1810 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1811 // "QSOP 3"    "",    "B",  "B",    "B",   "I",    "",     "",    ""
1812 
1813 // AZ, CA, MI, PA, VI
n3fjp_check_QP3()1814 static void n3fjp_check_QP3()
1815 {
1816 	try {
1817 		if (n3fjp_in_state) {
1818 			if (inpState->value()[0]) {
1819 				send_spcnum(inpState->value());
1820 //				string county = states.county(inpState->value(), inpCounty->value());
1821 //				if (!county.empty())
1822 //					send_county(county);
1823 			}
1824 			if (inpCounty->value()[0])
1825 				send_county(inpCounty->value());
1826 			if (inpVEprov->value()[0])
1827 				send_spcnum(inpVEprov->value());
1828 			if (cboCountry->value()[0] && (strcmp(cboCountry->value(), "USA") != 0))
1829 				send_spcnum(cboCountry->value());
1830 		} else {
1831 //			std::string stsh = states.state_short(inpState->value());
1832 //			std::string county = states.county(stsh, inpCounty->value());
1833 //			if (!county.empty()) {
1834 //				send_county(county);
1835 //			} else
1836 			if (inpCounty->value()[0])
1837 				send_county(inpCounty->value());
1838 		}
1839 		send_control("SERIALNOR", inpSerNo->value());
1840 	} catch (...) { throw; }
1841 
1842 	return;
1843 }
1844 
1845 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1846 // "QSOP 4"    "",    "B",  "B",    "",    "I",    "B",    "",    ""
1847 
1848 // CO, MN, NM
1849 /*
1850 N3FJP's Colorado QSO Party Contest Log
1851 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYSPCNUM</CONTROL><VALUE></VALUE></CMD>
1852 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYNAMER</CONTROL><VALUE></VALUE></CMD>
1853 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCOUNTYR</CONTROL><VALUE></VALUE></CMD>
1854 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCALL</CONTROL><VALUE></VALUE></CMD>
1855 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>LBLDIALOGUE</CONTROL><VALUE>Ready to begin!<crlf>
1856 */
n3fjp_check_QP4()1857 static void n3fjp_check_QP4()
1858 {
1859 	try {
1860 		if (n3fjp_in_state) {
1861 			std::string cntry = cboCountry->value();
1862 			if (inpState->value()[0])
1863 				send_spcnum(inpState->value());
1864 			else if (inpVEprov->value()[0])
1865 				send_spcnum(inpVEprov->value());
1866 			else if (!cntry.empty() && cntry != "USA")
1867 				send_county(inpCounty->value());
1868 			if (inpCounty->value()[0])
1869 				send_county(inpCounty->value());
1870 			if (inpName->value()[0])
1871 				send_name(inpName->value());
1872 		} else {
1873 			std::string st = QSOparties.qso_parties[progdefaults.SQSOcontest].state;
1874 //			send_county(states.county(st, inpCounty->value()));
1875 			send_county(inpCounty->value());
1876 			if (inpName->value()[0])
1877 				send_name(inpName->value());
1878 		}
1879 	} catch (...) { throw; }
1880 
1881 	return;
1882 }
1883 
1884 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1885 // "QSOP 5"    "B",   "I",  "B",    "",    "",     "",     "",    ""
1886 
1887 // SC, WV
n3fjp_check_QP5()1888 static void n3fjp_check_QP5()
1889 {
1890 	try {
1891 		if (n3fjp_in_state) {
1892 			if (inpState->value()[0])
1893 				send_spcnum(inpState->value());
1894 			else if (inpVEprov->value()[0])
1895 				send_spcnum(inpVEprov->value());
1896 			else if (cboCountry->value()[0])
1897 				send_spcnum(cboCountry->value());
1898 			if (inpCounty->value()[0])
1899 				send_county(inpCounty->value());
1900 		}else {
1901 			send_county(inpCounty->value());
1902 		}
1903 	} catch (...) { throw; }
1904 
1905 	return;
1906 }
1907 
1908 // "QSO Party","rRST","rST","rCNTY","rSER","rXCHG","rNAM","rCAT","STCTY"
1909 // "QSOP 6"    "B",   "B",  "B",    "",    "I",    "",     "",    ""
1910 
1911 // NC, WI
n3fjp_check_QP6()1912 static void n3fjp_check_QP6()
1913 {
1914 	try {
1915 		if (n3fjp_in_state) {
1916 			if (inpCounty->value()[0])
1917 				send_county(inpCounty->value());
1918 			if (inpState->value()[0])
1919 				send_spcnum(inpState->value());
1920 			else if (inpVEprov->value()[0])
1921 				send_spcnum(inpVEprov->value());
1922 			else if (cboCountry->value()[0])
1923 				send_spcnum(cboCountry->value());
1924 		} else {
1925 //			send_county(states.county(inpState->value(), inpCounty->value()));
1926 			send_county(inpCounty->value());
1927 		}
1928 	} catch (...) { throw; }
1929 	return;
1930 }
1931 
check_log_data()1932 static void check_log_data()
1933 {
1934 	try{
1935 	switch (n3fjp_contest) {
1936 		case FJP_FD:
1937 			n3fjp_check_FD();
1938 			break;
1939 		case FJP_WFD:
1940 			n3fjp_check_WFD();
1941 			break;
1942 		case FJP_KD:
1943 			n3fjp_check_KD();
1944 			break;
1945 		case FJP_ARR:
1946 			n3fjp_check_ARR();
1947 			break;
1948 		case FJP_RTTY:
1949 			n3fjp_check_RTTY();
1950 			break;
1951 		case FJP_ASCR:
1952 			n3fjp_check_ASCR();
1953 			break;
1954 		case FJP_JOTA:
1955 			n3fjp_check_JOTA();
1956 			break;
1957 		case FJP_CQ_WPX:
1958 			n3fjp_check_WPX();
1959 			break;
1960 		case FJP_IARI:
1961 			n3fjp_check_IARI();
1962 			break;
1963 		case FJP_NAS:
1964 			n3fjp_check_NAS();
1965 			break;
1966 		case FJP_CQWWRTTY:
1967 			n3fjp_check_CQWWRTTY();
1968 			break;
1969 		case FJP_CQWWDX:
1970 			n3fjp_check_CQWWDX();
1971 			break;
1972 		case FJP_SS:
1973 			n3fjp_check_SS();
1974 			break;
1975 		case FJP_NAQP:
1976 			n3fjp_check_NAQP();
1977 			break;
1978 		case FJP_1010:
1979 			n3fjp_check_1010();
1980 			break;
1981 		case FJP_AIDX:
1982 			n3fjp_check_AIDX();
1983 			break;
1984 		case FJP_AICW:
1985 			n3fjp_check_AICW();
1986 			break;
1987 		case FJP_VHF:
1988 			n3fjp_check_VHF();
1989 			break;
1990 		case FJP_WAE:
1991 			n3fjp_check_WAE();
1992 			break;
1993 		case FJP_MDQP:
1994 			n3fjp_check_MDQSP();
1995 			break;
1996 		case FJP_7QP:
1997 			n3fjp_check_7QP();
1998 			break;
1999 		case FJP_NEQP:
2000 			n3fjp_check_NEQP();
2001 			break;
2002 		case FJP_QP1:
2003 			n3fjp_check_QP1();
2004 			break;
2005 		case FJP_QP2:
2006 			n3fjp_check_QP2();
2007 			break;
2008 		case FJP_QP3:
2009 			n3fjp_check_QP3();
2010 			break;
2011 		case FJP_QP4:
2012 			n3fjp_check_QP4();
2013 			break;
2014 		case FJP_QP5:
2015 			n3fjp_check_QP5();
2016 			break;
2017 		case FJP_QP6:
2018 			n3fjp_check_QP6();
2019 			break;
2020 		case FJP_ACL:
2021 		case FJP_NONE:
2022 		default:
2023 			n3fjp_check_GENERIC();
2024 	}
2025 	} catch (...) { throw; }
2026 }
2027 
n3fjp_dupcheck()2028 int n3fjp_dupcheck()
2029 {
2030 	guard_lock rx_lock(&n3fjp_mutex);
2031 
2032 	string chkcall = inpCall->value();
2033 
2034 	if (chkcall.length() < 3) return false;
2035 	if ((chkcall.length() == 3) && isdigit(chkcall[2])) return false;
2036 
2037 	string cmd;
2038 
2039 	try {
2040 		send_call(chkcall);
2041 		send_band(strip(n3fjp_opband()));
2042 		send_mode(n3fjp_tstmode());
2043 		n3fjp_sendRSTS(inpRstOut->value());
2044 		n3fjp_sendRSTR(inpRstIn->value());
2045 		check_log_data();
2046 
2047 		send_action("CALLTAB");
2048 	} catch (...) { throw; }
2049 	return 0;
2050 }
2051 
send_log_data()2052 static void send_log_data()
2053 {
2054 	send_call(rec.getField(CALL));
2055 	send_band(strip(n3fjp_opband()));
2056 	send_freq(n3fjp_freq());
2057 	send_mode(n3fjp_opmode());
2058 	n3fjp_sendRSTS(rec.getField(RST_SENT));
2059 	n3fjp_sendRSTR(rec.getField(RST_RCVD));
2060 
2061 	try {
2062 		switch (n3fjp_contest) {
2063 			case FJP_NONE:
2064 				n3fjp_send_NONE();
2065 				break;
2066 			case FJP_FD:
2067 				n3fjp_send_FD();
2068 				break;
2069 			case FJP_WFD:
2070 				n3fjp_send_WFD();
2071 				break;
2072 			case FJP_KD:
2073 				n3fjp_send_KD();
2074 				break;
2075 			case FJP_ARR:
2076 				n3fjp_send_ARR();
2077 				break;
2078 			case FJP_RTTY:
2079 				n3fjp_send_RTTY();
2080 				break;
2081 			case FJP_ASCR:
2082 				n3fjp_send_ASCR();
2083 				break;
2084 			case FJP_JOTA:
2085 				n3fjp_send_JOTA();
2086 				break;
2087 			case FJP_CQ_WPX:
2088 				n3fjp_send_WPX();
2089 				break;
2090 			case FJP_IARI:
2091 				n3fjp_send_IARI();
2092 				break;
2093 			case FJP_NAS:
2094 				n3fjp_send_NAS();
2095 				break;
2096 			case FJP_CQWWRTTY:
2097 				n3fjp_send_CQWWRTTY();
2098 				break;
2099 			case FJP_CQWWDX:
2100 				n3fjp_send_CQWWDX();
2101 				break;
2102 			case FJP_SS:
2103 				n3fjp_send_SS();
2104 				break;
2105 			case FJP_NAQP:
2106 				n3fjp_send_NAQP();
2107 				break;
2108 			case FJP_1010:
2109 				n3fjp_send_1010();
2110 				break;
2111 			case FJP_AIDX:
2112 				n3fjp_send_AIDX();
2113 				break;
2114 			case FJP_AICW:
2115 				n3fjp_send_AICW();
2116 				break;
2117 			case FJP_VHF:
2118 				n3fjp_send_VHF();
2119 				break;
2120 			case FJP_WAE:
2121 				n3fjp_send_WAE();
2122 				break;
2123 			case FJP_MDQP:
2124 				n3fjp_send_MDQSP();
2125 				break;
2126 			case FJP_7QP:
2127 				n3fjp_send_7QP();
2128 				break;
2129 			case FJP_NEQP:
2130 				n3fjp_send_NEQP();
2131 				break;
2132 			case FJP_QP1:
2133 				n3fjp_send_QP1();
2134 				break;
2135 			case FJP_QP2:
2136 				n3fjp_send_QP2();
2137 				break;
2138 			case FJP_QP3:
2139 				n3fjp_send_QP3();
2140 				break;
2141 			case FJP_QP4:
2142 				n3fjp_send_QP4();
2143 				break;
2144 			case FJP_QP5:
2145 				n3fjp_send_QP5();
2146 				break;
2147 			case FJP_QP6:
2148 				n3fjp_send_QP6();
2149 				break;
2150 			case FJP_ACL:
2151 				n3fjp_send_NONE();
2152 				break;
2153 			default:
2154 				n3fjp_send_GENERIC();
2155 				break;
2156 		}
2157 		send_command("NEXTSERIALNUMBER");
2158 	} catch (...) { throw; }
2159 }
2160 
enter_log_data()2161 static void enter_log_data()
2162 {
2163 	try {
2164 		send_log_data();
2165 		send_action("ENTER");
2166 //		if (n3fjp_contest != FJP_SS) {
2167 //			string other = "XCVR:";
2168 //			char szfreq[6];
2169 //			snprintf(szfreq, sizeof(szfreq), "%d", (int)active_modem->get_txfreq());
2170 //			other.append(ModeIsLSB(rec.getField(ADIF_MODE)) ? "LSB" : "USB");
2171 //			other.append(" MODE:");
2172 //			other.append(strip(rec.getField(ADIF_MODE)));
2173 //			other.append(" WF:");
2174 //			other.append(szfreq);
2175 //			send_control("OTHER8", other);
2176 //		}
2177 
2178 	} catch (...) { throw; }
2179 }
2180 
send_data()2181 static void send_data()
2182 {
2183 	try {
2184 
2185 		send_command("IGNORERIGPOLLS", "TRUE");
2186 
2187 		send_call(rec.getField(CALL));
2188 		send_freq(n3fjp_freq());
2189 		send_band(n3fjp_opband());
2190 		send_mode(n3fjp_opmode());
2191 
2192 		enter_log_data();
2193 
2194 		send_command("IGNORERIGPOLLS", "FALSE");
2195 
2196 	} catch (...) { throw; }
2197 }
2198 
send_data_norig()2199 static void send_data_norig()
2200 {
2201 	try {
2202 		string cmd;
2203 
2204 		send_call(rec.getField(CALL));
2205 		cmd = "<CMD><CHANGEBM>";
2206 		cmd.append("<BAND>").append(n3fjp_opband()).append("</BAND>");
2207 		cmd.append("<MODE>").append(n3fjp_opmode()).append("</MODE>");
2208 		cmd.append("</CMD>");
2209 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
2210 
2211 		enter_log_data();
2212 
2213 	} catch (...) { throw; }
2214 }
2215 
get_n3fjp_frequency()2216 void get_n3fjp_frequency()
2217 {
2218 	try {
2219 		send_command("READBMF");
2220 	} catch (...) { throw; }
2221 }
2222 
do_n3fjp_add_record_entries()2223 void do_n3fjp_add_record_entries()
2224 {
2225 	if(!n3fjp_socket) return;
2226 	if (!n3fjp_connected) return;
2227 
2228 	string cmd, response, val;
2229 
2230 	try {
2231 		if (n3fjp_has_xcvr_control == N3FJP)
2232 			send_data();
2233 		else
2234 			send_data_norig();
2235 
2236 	} catch (const SocketException& e) {
2237 		result.str("");
2238 		result << "Error: " << e.error() << ", " << e.what();
2239 		n3fjp_print(result.str());
2240 		throw e;
2241 	}
2242 	n3fjp_bool_add_record = false;
2243 }
2244 
n3fjp_set_freq(long f)2245 void n3fjp_set_freq(long f)
2246 {
2247 	char szfreq[20];
2248 	snprintf(szfreq, sizeof(szfreq), "%ld", f);
2249 	string freq = szfreq;
2250 	while (freq.length() < 7) freq.insert(0, "0");
2251 	freq.insert(freq.length() - 6, ".");
2252 
2253 	string cmd;
2254 
2255 	cmd.assign("<CMD><CHANGEFREQ><VALUE>");
2256 	cmd.append(freq);
2257 	cmd.append("</VALUE><SUPPRESSMODEDEFAULT>TRUE</SUPPRESSMODEDEFAULT></CMD>");
2258 
2259 	{	guard_lock send_lock(&send_this_mutex);
2260 		send_this = cmd;
2261 	}
2262 }
2263 
n3fjp_set_ptt(int on)2264 void n3fjp_set_ptt(int on)
2265 {
2266 	if (n3fjp_has_xcvr_control != N3FJP) return;
2267 
2268 	string cmd = "<CMD>";
2269 	if (on) {
2270 		if (progdefaults.enable_N3FJP_RIGTX)
2271 			cmd.append("<RIGTX>");
2272 		else
2273 			cmd.append("<CWCOMPORTKEYDOWN>");
2274 	} else {
2275 		if (progdefaults.enable_N3FJP_RIGTX)
2276 			cmd.append("<RIGRX>");
2277 		else
2278 			cmd.append("<CWCOMPORTKEYUP>");
2279 	}
2280 	cmd.append("</CMD>");
2281 
2282 	{	guard_lock send_lock(&send_this_mutex);
2283 		send_this = cmd;
2284 	}
2285 }
2286 
n3fjp_add_record(cQsoRec & record)2287 void n3fjp_add_record(cQsoRec &record)
2288 {
2289 	if (!n3fjp_connected) return;
2290 	rec = record;
2291 	n3fjp_bool_add_record = true;
2292 }
2293 
2294 std::string n3fjp_serno = "";
2295 
n3fjp_parse_next_serial(string buff)2296 void n3fjp_parse_next_serial(string buff)
2297 {
2298 	n3fjp_serno = ParseValueField("NEXTSERIALNUMBERRESPONSE", buff);
2299 	updateOutSerNo();
2300 }
2301 
2302 //======================================================================
2303 //
2304 //======================================================================
n3fjp_parse_response(string tempbuff)2305 void n3fjp_parse_response(string tempbuff)
2306 {
2307 	if (tempbuff.empty()) return;
2308 	size_t p1 = string::npos, p2 = string::npos;
2309 
2310 	if (tempbuff.find("RIGRESPONSE") != string::npos) {
2311 		size_t p0 = tempbuff.find("<RIG>");
2312 		if (p0 != string::npos) {
2313 			p0 += strlen("<RIG>");
2314 			string rigname = tempbuff.substr(p0);
2315 			p0 = rigname.find("</RIG>");
2316 			if (p0 != string::npos) {
2317 				rigname.erase(p0);
2318 				if (rigname != "None" && rigname != "Client API") {
2319 					n3fjp_has_xcvr_control = N3FJP;
2320 					send_command("READBMF");
2321 				} else
2322 				n3fjp_has_xcvr_control = FLDIGI;
2323 			}
2324 		}
2325 	}
2326 
2327 	if (n3fjp_has_xcvr_control == N3FJP) {
2328 		if ((p1 = tempbuff.find("<CHANGEFREQ><VALUE>")) != string::npos) {
2329 			p1 += strlen("<CHANGEFREQ><VALUE>");
2330 			p2 = tempbuff.find("</VALUE>", p1);
2331 			if (p2 == string::npos) return;
2332 			string sfreq = tempbuff.substr(p1, p2 - p1);
2333 			REQ(adjust_freq, sfreq);
2334 		} else if (tempbuff.find("<READBMFRESPONSE>") != string::npos) {
2335 			string sfreq = ParseField(tempbuff, "FREQ");
2336 			REQ(adjust_freq, sfreq);
2337 		}
2338 	}
2339 
2340 	if (tempbuff.find("<CALLTABEVENT>") != string::npos) {
2341 		n3fjp_rxbuffer = tempbuff;
2342 		REQ(n3fjp_parse_calltab_event, tempbuff);
2343 	}
2344 
2345 	if (tempbuff.find("ALLFIELDSWVRESPONSE") != string::npos) {
2346 		REQ(n3fjp_parse_data_stream, tempbuff);
2347 	}
2348 	if (tempbuff.find("<ENTEREVENT>") != string::npos) {
2349 		send_command("NEXTSERIALNUMBER");
2350 	}
2351 	if (tempbuff.find("<NEXTSERIALNUMBERRESPONSE>") != string::npos) {
2352 		REQ(n3fjp_parse_next_serial, tempbuff);
2353 	}
2354 
2355 	if (tempbuff.find("CALLTABDUPEEVENT") != string::npos &&
2356 		tempbuff.find("Duplicate") != string::npos) {
2357 			if (tempbuff.find("Possible") != string::npos)
2358 				REQ(show_dup, (void*)2);
2359 			else
2360 				REQ(show_dup, (void*)1);
2361 	}
2362 }
2363 
2364 //======================================================================
2365 //
2366 //======================================================================
n3fjp_rcv_data()2367 void n3fjp_rcv_data()
2368 {
2369 	string tempbuff = "";
2370 	try {
2371 		n3fjp_rcv(tempbuff, progdefaults.enable_N3FJP_log);
2372 		n3fjp_parse_response(tempbuff);
2373 	} catch (const SocketException& e) {
2374 		result.str("");
2375 		result << "n3fjp_rcv_data()::failed " << e.error() << " " << e.what();
2376 		n3fjp_print(result.str());
2377 		throw e;
2378 	} catch (...) { throw; }
2379 }
2380 
2381 static int logger_nbr = 0;
2382 
match(std::string s1,std::string s2)2383 inline bool match(std::string s1, std::string s2)
2384 {
2385 	return (s1.find(s2) != std::string::npos ||
2386 			s2.find(s1) != std::string::npos);
2387 		}
2388 
select_fldigi_logging()2389 static void select_fldigi_logging()
2390 {
2391 //	check for specific contest
2392 	size_t n = 0;
2393 	std::string logger = n3fjp_logger[logger_nbr].program;
2394 
2395 n3fjp_print(std::string("logger: ").append(logger));
2396 
2397 	if (logger == "Amateur Contact Log") {
2398 		listbox_contest->index(0);
2399 		progdefaults.logging = 0;
2400 		UI_select();
2401 		return;
2402 	}
2403 
2404 	if ((n = logger.find("Jamboree") != std::string::npos)) {
2405 		logger.insert(0, "ARRL ");
2406 	}
2407 	if (logger.find("CQ WW DX RTTY") != std::string::npos)
2408 		logger = "WW DX RTTY";
2409 
2410 	progdefaults.CONTESTnotes = "";
2411 
2412 	for (int n = 2; !contests[n].name.empty(); n++) {
2413 		if (match(contests[n].name, logger)) {
2414 			progdefaults.logging = n;
2415 			listbox_contest->index(n);
2416 			listbox_QP_contests->index(0);
2417 			UI_select();
2418 			progdefaults.CONTESTnotes = contests[progdefaults.logging].notes;
2419 			inp_contest_notes->value(progdefaults.CONTESTnotes.c_str());
2420 n3fjp_print(std::string("found: ").append(contests[n].name));
2421 			return;
2422 		}
2423 	}
2424 n3fjp_print(std::string("Check for SQSO: ").append(logger));
2425 
2426 	if ((n = logger.find("QP Contest Log")) != std::string::npos) {
2427 		logger.erase(n + 2, 12);
2428 		progdefaults.logging = LOG_SQSO;
2429 		listbox_contest->index(progdefaults.logging);
2430 n3fjp_print(std::string("Out of state SQSO log: ").append(logger));
2431 	}
2432 	else if ((n = logger.find(" QSO Party Contest Log")) != std::string::npos) {
2433 		logger.erase(n + 10);
2434 		progdefaults.logging = LOG_SQSO;
2435 		listbox_contest->index(progdefaults.logging);
2436 n3fjp_print(std::string("In state SQSO log: ").append(logger));
2437 	}
2438 
2439 	for (n = 1; QSOparties.qso_parties[n].contest[0]; n++) {
2440 		if (logger == QSOparties.qso_parties[n].contest) {
2441 n3fjp_print(std::string("QSOparty: ").append(QSOparties.qso_parties[n].contest));
2442 			progdefaults.SQSOcontest =  n;
2443 			listbox_QP_contests->index(progdefaults.SQSOcontest - 1);
2444 			inp_contest_notes->value(progdefaults.CONTESTnotes.c_str());
2445 			progdefaults.CONTESTnotes = QSOparties.qso_parties[progdefaults.SQSOcontest].notes;
2446 			adjust_for_contest(0);
2447 		}
2448 	}
2449 	inp_contest_notes->value(progdefaults.CONTESTnotes.c_str());
2450 
2451 	UI_select();
2452 	clearQSO();
2453 	return;
2454 }
2455 
fldigi_no_contest()2456 static void fldigi_no_contest()
2457 {
2458 	progdefaults.SQSOcontest = 0;//1;
2459 	progdefaults.logging = 0;
2460 	adjust_for_contest(0);
2461 	UI_select();
2462 	set_log_colors();
2463 	clearQSO();
2464 	listbox_contest->index(0);
2465 }
2466 
2467 static int connect_tries = 0;
2468 
connect_to_n3fjp_server()2469 static bool connect_to_n3fjp_server()
2470 {
2471 	try {
2472 		n3fjp_serno.clear();
2473 
2474 		if (!n3fjp_connected)
2475 			n3fjp_socket->connect();
2476 
2477 		if (!n3fjp_socket->is_connected()) {
2478 			MilliSleep(200);
2479 			n3fjp_socket->connect();
2480 			if (!n3fjp_socket->is_connected()) {
2481 				if (!connect_tries--) {
2482 					result.str("");
2483 					result << "Cannot connect to server: " << n3fjp_socket->fd();
2484 					n3fjp_print(result.str());
2485 					connect_tries = 20;
2486 				}
2487 				return false;
2488 			}
2489 			result.str("");
2490 			result << "connected to n3fjp server: " << n3fjp_socket->fd();
2491 			n3fjp_print(result.str());
2492 			connect_tries = 0;
2493 		}
2494 
2495 		string buffer;
2496 
2497 		string cmd = "<CMD><PROGRAM></CMD>";
2498 		n3fjp_send(cmd, true);
2499 
2500 		buffer.clear();
2501 		size_t n;
2502 		for (n = 0; n < 10; n++) {
2503 			n3fjp_rcv(buffer, true);
2504 			if (!buffer.empty()) break;
2505 		}
2506 		if (buffer.empty()) {
2507 			n3fjp_print("N3FJP logger not responding");
2508 			return false;
2509 		}
2510 
2511 		string info = ParseField(buffer, "PGM");
2512 		connected_to = info;
2513 
2514 		n3fjp_contest = FJP_NONE;
2515 
2516 		n = info.find("N3FJP's ");
2517 		if (n != std::string::npos) info.erase(n, 8);
2518 
2519 		if (info.find("Winter") != std::string::npos)
2520 			info = "Winter FD";
2521 
2522 		n3fjp_print(std::string("Info: ").append(info));
2523 
2524 		for (n = 0; n < sizeof(n3fjp_logger) / sizeof(*n3fjp_logger); n++) {
2525 			if (info.find(n3fjp_logger[n].program) == 0) {
2526 				n3fjp_contest = n3fjp_logger[n].contest;
2527 				n3fjp_in_state = n3fjp_logger[n].in_state;
2528 				logger_nbr = n;
2529 				REQ(select_fldigi_logging);
2530 				break;
2531 			}
2532 		}
2533 		if (n == sizeof(n3fjp_logger) / sizeof(*n3fjp_logger)) {
2534 			n3fjp_print(string(info).append(" not supported by fldigi"));
2535 			return false;
2536 		}
2537 		else
2538 			n3fjp_print(std::string("Connected to: ").append(n3fjp_logger[n].program));
2539 
2540 		send_command("NEXTSERIALNUMBER");
2541 
2542 		info.insert(0, "Connected to ");
2543 
2544 		string ver = ParseField(buffer, "VER");
2545 		info.append(", Ver ").append(ver);
2546 
2547 		n3fjp_connected = true;
2548 		REQ(set_connect_box);
2549 
2550 		cmd = " <CMD><VISIBLEFIELDS></CMD>";
2551 		n3fjp_send(cmd, true);
2552 
2553 		buffer.clear();
2554 		n3fjp_rcv(buffer, true);
2555 
2556 		cmd = "<CMD><CALLTABENTEREVENTS><VALUE>TRUE</VALUE></CMD>";
2557 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
2558 
2559 		cmd = "<CMD><READOFFSETENABLED></CMD>";
2560 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
2561 
2562 		cmd = "<CMD><READOFFSET></CMD>";
2563 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
2564 
2565 		cmd = "<CMD><READMODEDEFAULTSUPPRESS></CMD>";
2566 		n3fjp_send(cmd, progdefaults.enable_N3FJP_log);
2567 
2568 		cmd = "RIGENABLED";
2569 		send_command(cmd);
2570 
2571 	} catch (const SocketException& e) {
2572 		result.str("");
2573 		result << e.what() << "(" << e.error() << ")";
2574 		n3fjp_print(result.str());
2575 		connected_to.clear();
2576 		LOG_ERROR("%s", result.str().c_str());
2577 	} catch (...) {
2578 		n3fjp_print("Caught unknown error");
2579 		LOG_ERROR("%s", "Caught unknown error");
2580 		connected_to.clear();
2581 	}
2582 	return true;
2583 }
2584 
2585 //======================================================================
2586 //
2587 //======================================================================
2588 
n3fjp_start()2589 void n3fjp_start()
2590 {
2591 	n3fjp_ip_address =  progdefaults.N3FJP_address;
2592 	n3fjp_ip_port = progdefaults.N3FJP_port;
2593 
2594 	try {
2595 		if (n3fjp_socket) delete n3fjp_socket;
2596 
2597 		n3fjp_socket = new Socket(
2598 				Address( n3fjp_ip_address.c_str(),
2599 						 n3fjp_ip_port.c_str(),
2600 						 "tcp") );
2601 		if (!n3fjp_socket) return;
2602 
2603 		n3fjp_socket->set_timeout(0.20);//0.05);
2604 		n3fjp_socket->set_nonblocking(true);
2605 
2606 		result.str("");
2607 		result << "Client socket " << n3fjp_socket->fd();
2608 		n3fjp_print(result.str());
2609 	}
2610 	catch (const SocketException& e) {
2611 		result.str("");
2612 		result << e.what() << "(" << e.error() << ")";
2613 		n3fjp_print(result.str());
2614 		LOG_ERROR("%s", result.str().c_str() );
2615 		delete n3fjp_socket;
2616 		n3fjp_socket = 0;
2617 		n3fjp_connected = false;
2618 		REQ(set_connect_box);
2619 		n3fjp_has_xcvr_control = UNKNOWN;
2620 	} catch (...) {
2621 		n3fjp_print("Caught unknown error");
2622 		n3fjp_print(result.str());
2623 		LOG_ERROR("%s", result.str().c_str() );
2624 		delete n3fjp_socket;
2625 		n3fjp_socket = 0;
2626 		n3fjp_connected = false;
2627 		REQ(set_connect_box);
2628 		n3fjp_has_xcvr_control = UNKNOWN;
2629 	}
2630 }
2631 
2632 //======================================================================
2633 // Disconnect from N3FJP tcpip server
2634 //======================================================================
n3fjp_disconnect(bool clearlog)2635 void n3fjp_disconnect(bool clearlog)
2636 {
2637 	if (n3fjp_socket) {
2638 		n3fjp_send("", false);//progdefaults.enable_N3FJP_log);
2639 		delete n3fjp_socket;
2640 		n3fjp_socket = 0;
2641 	}
2642 	n3fjp_connected = false;
2643 	n3fjp_has_xcvr_control = UNKNOWN;
2644 	n3fjp_serno.clear();
2645 	connected_to.clear();
2646 	REQ(set_connect_box);
2647 	if (clearlog) REQ(fldigi_no_contest);
2648 	n3fjp_print("Disconnected");
2649 
2650 }
2651 
2652 //======================================================================
2653 // Thread loop
2654 //======================================================================
2655 
n3fjp_loop(void * args)2656 void *n3fjp_loop(void *args)
2657 {
2658 	SET_THREAD_ID(N3FJP_TID);
2659 
2660 	int loopcount = 9;
2661 	int n3fjp_looptime = 100; // initially 0.1 second delay to connect
2662 	while(1) {
2663 		if (n3fjp_exit) break;
2664 
2665 		MilliSleep(10);
2666 		if (n3fjp_wait) n3fjp_wait -= 10;
2667 		if (n3fjp_looptime) n3fjp_looptime -= 10;
2668 
2669 		if (n3fjp_wait > 0) continue;
2670 
2671 		if (n3fjp_looptime > 0) continue;
2672 
2673 		n3fjp_looptime = 250;  // r/w to N3FJP logger every 1/4 second
2674 
2675 		loopcount = (loopcount + 1) % 10;
2676 		if (progdefaults.connect_to_n3fjp) {
2677 			if (!n3fjp_socket || (n3fjp_socket->fd() == -1))
2678 				n3fjp_start();
2679 
2680 			else {
2681 				if ((n3fjp_ip_address != progdefaults.N3FJP_address) ||
2682 					(n3fjp_ip_port != progdefaults.N3FJP_port) ) {
2683 					n3fjp_disconnect(true);
2684 					n3fjp_start();
2685 				}
2686 
2687 				if (!n3fjp_connected) {
2688 					if (loopcount == 0)
2689 						if (!connect_to_n3fjp_server())
2690 							n3fjp_disconnect(false);
2691 				} else try {  // insure connection still up (2.5 second interval)
2692 					if (loopcount == 0) {
2693 						guard_lock send_lock(&send_this_mutex);
2694 						std::string buffer;
2695 						string cmd = "<CMD><PROGRAM></CMD>";
2696 						n3fjp_send(cmd, false);
2697 						size_t n;
2698 						for (n = 0; n < 10; n++) {
2699 							n3fjp_rcv(buffer, false);
2700 							if (!buffer.empty()) break;
2701 						}
2702 						if (buffer.empty()) {
2703 							n3fjp_print(std::string("Lost server connection to ").append(connected_to));
2704 							n3fjp_disconnect(true);
2705 							continue;
2706 						}
2707 					}
2708 					if (n3fjp_has_xcvr_control == FLDIGI)
2709 						n3fjp_send_freq_mode();
2710 					if (!send_this.empty()) {
2711 						guard_lock send_lock(&send_this_mutex);
2712 						n3fjp_send(send_this, progdefaults.enable_N3FJP_log);
2713 						send_this.clear();
2714 					} else if (n3fjp_bool_add_record)
2715 						do_n3fjp_add_record_entries();
2716 					else {
2717 						guard_lock rx_lock(&n3fjp_mutex);
2718 						n3fjp_rcv_data();
2719 					}
2720 				} catch (const SocketException& e) {
2721 					result.str("");
2722 					result << "Error: " << e.error() << ", " << e.what();
2723 					n3fjp_print(result.str());
2724 					n3fjp_disconnect(true);
2725 				} catch (...) {
2726 					n3fjp_print("Caught unknown error");
2727 					n3fjp_disconnect(true);
2728 				}
2729 			}
2730 		} else if (n3fjp_connected)
2731 			n3fjp_disconnect(true);
2732 	}
2733 	// exit the n3fjp thread
2734 	SET_THREAD_CANCEL();
2735 	return NULL;
2736 }
2737 
2738 //======================================================================
2739 //
2740 //======================================================================
2741 
n3fjp_init(void)2742 void n3fjp_init(void)
2743 {
2744 	n3fjp_enabled = false;
2745 	n3fjp_exit = false;
2746 
2747 	if (pthread_create(&n3fjp_thread, NULL, n3fjp_loop, NULL) < 0) {
2748 		LOG_ERROR("pthread_create failed");
2749 		return;
2750 	}
2751 
2752 	LOG_INFO("N3FJP logger thread started");
2753 
2754 	pathname = DebugDir;
2755 	pathname.append("n3fjp_data_stream.txt");
2756 	rotate_log(pathname);
2757 	FILE *n3fjplog = fl_fopen(pathname.c_str(), "w");
2758 	fprintf(n3fjplog, "N3FJP / fldigi tcpip log\n\n");
2759 	fclose(n3fjplog);
2760 
2761 	n3fjp_enabled = true;
2762 }
2763 
2764 //======================================================================
2765 //
2766 //======================================================================
n3fjp_close(void)2767 void n3fjp_close(void)
2768 {
2769 	if (!n3fjp_enabled) return;
2770 
2771 	guard_lock close_lock(&n3fjp_socket_mutex);
2772 
2773 	n3fjp_exit = true;
2774 	CANCEL_THREAD(n3fjp_thread);
2775 	pthread_join(n3fjp_thread, NULL);
2776 	n3fjp_enabled = false;
2777 
2778 	LOG_INFO("%s", "N3FJP logger thread terminated. ");
2779 
2780 	if(n3fjp_socket) {
2781 		delete n3fjp_socket;
2782 		n3fjp_socket = 0;
2783 	}
2784 
2785 }
2786 
2787 /*
2788 FLDIGI log fields
2789 
2790 FREQ           QSO frequency in Mhz
2791 CALL           contacted stations CALLSIGN
2792 MODE           QSO mode
2793 NAME           contacted operators NAME
2794 QSO_DATE       QSO date
2795 QSO_DATE_OFF   QSO date OFF, according to ADIF 2.2.6
2796 TIME_OFF       HHMM or HHMMSS in UTC
2797 TIME_ON        HHMM or HHMMSS in UTC
2798 QTH            contacted stations city
2799 RST_RCVD       received signal report
2800 RST_SENT       sent signal report
2801 STATE          contacted stations STATE
2802 VE_PROV        2 letter abbreviation for Canadian Province
2803 NOTES          QSO notes
2804 
2805 QSLRDATE       QSL received date
2806 QSLSDATE       QSL sent date
2807 
2808 EQSLRDATE      EQSL received date
2809 EQSLSDATE      EQSL sent date
2810 
2811 LOTWRDATE      EQSL received date
2812 LOTWSDATE      EQSL sent date
2813 
2814 GRIDSQUARE     contacted stations Maidenhead Grid Square
2815 BAND           QSO band
2816 CNTY           secondary political subdivision, ie: county
2817 COUNTRY        contacted stations DXCC entity name
2818 CQZ            contacted stations CQ Zone
2819 DXCC           contacted stations Country Code
2820 QSL_VIA        contacted stations QSL manager
2821 IOTA           Islands on the air
2822 ITUZ           ITU zone
2823 CONT           contacted stations continent
2824 
2825 SRX            received serial number for a contest QSO
2826 STX            QSO transmitted serial number
2827 
2828 XCHG1          contest exchange received
2829 MYXCHG         contest exchange sent
2830 
2831 CLASS        Field Day class received
2832 ARRL_SECT      Field Day section received
2833 
2834 TX_PWR         power transmitted by this station
2835 
2836 OP_CALL       Callsign of person logging the QSO
2837 STA_CALL      Callsign of transmitting station
2838 MY_GRID       Xmt station locator
2839 MY_CITY       Xmt station city
2840 
2841 SS_SEC        CW sweepstakes section
2842 SS_SERNO      CW sweepstakes serial number received
2843 SS_PREC       CW sweepstakes precedence
2844 SS_CHK        CW sweepstakes check
2845 
2846 AGE           contacted operators age in years
2847 TEN_TEN       contacted stations ten ten #
2848 CHECK         contacted stations contest identifier
2849 
2850 |-------------------------------------------------------------|
2851 | N3FJP field                    | FLDIGI LOG FIELD           |
2852 |--------------------------------|----------------------------|
2853 | txtEntry1010                   | rec.getField(TEN_TEN)      |
2854 | txtEntryCall                   | rec.getField(CALL)         |
2855 | txtEntryCheck (Rookie Roundup) | rec.getField(CHECK)        |
2856 | txtEntryCheck (1010 ??)      ) | rec.getField(CHECK)        |
2857 | txtEntryClass                  | rec.getField(CLASS)      |
2858 | txtEntryCountyR                | rec.getField(CNTY)         |
2859 | txtEntryGrid                   | rec.getField(GRIDSQUARE)   |
2860 | txtEntryNameR                  | rec.getField(NAME)         |
2861 | txtEntryRSTR                   | rec.getField(RST_RCVD)     |
2862 | txtEntryRSTS                   | rec.getField(RST_SENT)     |
2863 | txtEntrySection                | rec.getField(ARRL_SECT)    |
2864 | txtEntrySection                | rec.getField(SS_SEC)       |
2865 | txtEntrySerialNoT              | rec.getField(STX)          |
2866 | txtEntrySerialNoR              | rec.getField(SRX)          |
2867 | txtEntrySpcNum                 | rec.getField(XCHG1)        |
2868 | txtEntryState                  | rec.getField(STATE)        |
2869 | txtEntryState                  | rec.getField(VE_PROV) ??   |
2870 |-------------------------------------------------------------|
2871 
2872 Use this command to query N3FJP logger to find which fields are visible
2873 for any given program.
2874 
2875 <CMD><VISIBLEFIELDS></CMD>
2876 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCOUNTYR</CONTROL><VALUE></VALUE></CMD>
2877 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYSTATE</CONTROL><VALUE></VALUE></CMD>
2878 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCOUNTRYWORKED</CONTROL><VALUE></VALUE></CMD>
2879 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYMODE</CONTROL><VALUE></VALUE></CMD>
2880 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYBAND</CONTROL><VALUE></VALUE></CMD>
2881 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYDATE</CONTROL><VALUE></VALUE></CMD>
2882 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYTIMEOFF</CONTROL><VALUE></VALUE></CMD>
2883 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYTIMEON</CONTROL><VALUE></VALUE></CMD>
2884 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYRSTS</CONTROL><VALUE></VALUE></CMD>
2885 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYRSTR</CONTROL><VALUE></VALUE></CMD>
2886 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYPOWER</CONTROL><VALUE></VALUE></CMD>
2887 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYOTHER1</CONTROL><VALUE></VALUE></CMD>
2888 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYNAMER</CONTROL><VALUE></VALUE></CMD>
2889 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYFREQUENCY</CONTROL><VALUE></VALUE></CMD>
2890 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCOMMENTS</CONTROL><VALUE></VALUE></CMD>
2891 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>TXTENTRYCALL</CONTROL><VALUE></VALUE></CMD>
2892 <CMD><VISIBLEFIELDSRESPONSE><CONTROL>LBLDIALOGUE</CONTROL><VALUE>Ready to begin!</VALUE></CMD>
2893 
2894 Africa All Mode: RST / Serial Received
2895 ARRL Field Day: Class / Section
2896 ARRL Kids Day: Name / Age / SPCNum
2897 ARRL Rookie Roundup: Name / Check / SPCNum
2898 ARRL RTTY: RST / SPCNum
2899 ARRL School Club Roundup: RST / Class / SPCNum / Name (optional)
2900 CQ WPX: RST / Serial Received
2901 CQ WW RTTY: RST / CQ Zone (autofilled from call) / State (or Province)
2902 Italian ARI International DX: RST / SPCNum
2903 Jamboree On The Air (lots of unrequired fields)
2904 NCJ North American QSO Party: Name / SPCNum
2905 NCJ North American Sprint: Serial Received / Name / SPCNum
2906 State QSO Parties, varies:
2907 	Name
2908 	State
2909 	County
2910 	Serial Received
2911 	Section
2912 	SPCNum
2913 Ten Ten: Name / SPCNum / 1010 number
2914 VHF: Grid / RST
2915 Worked All Europe: RST / Serial Received (QTCs not coded in API)
2916 Winter Field Day: Category (use class, just like ARRL Field Day) / Section
2917 
2918 */
2919