1 // =====================================================================
2 //
3 // maclogger.cxx
4 //
5 // receive log data from maclogger udp broadcast message
6 //
7 // Copyright (C) 2016
8 //		Dave Freese, W1HKJ
9 //
10 // This file is part of fldigi.
11 //
12 // Fldigi is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // Fldigi is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
24 // =====================================================================
25 
26 #include <iostream>
27 #include <cmath>
28 #include <cstring>
29 #include <vector>
30 #include <list>
31 #include <stdlib.h>
32 
33 #include <FL/Fl_Text_Display.H>
34 #include <FL/Fl_Text_Buffer.H>
35 
36 #include "rigsupport.h"
37 #include "modem.h"
38 #include "trx.h"
39 #include "fl_digi.h"
40 #include "configuration.h"
41 #include "main.h"
42 #include "waterfall.h"
43 #include "macros.h"
44 #include "qrunner.h"
45 #include "debug.h"
46 #include "status.h"
47 #include "icons.h"
48 
49 #include "maclogger.h"
50 
51 #include "confdialog.h"
52 
53 LOG_FILE_SOURCE(debug::LOG_MACLOGGER);
54 
55 using namespace std;
56 
57 //======================================================================
58 // Socket MACLOGGER i/o used on all platforms
59 //======================================================================
60 
61 pthread_t maclogger_thread;
62 pthread_t maclogger_rx_socket_thread;
63 pthread_mutex_t mclg_str_mutex     = PTHREAD_MUTEX_INITIALIZER;
64 Socket *maclogger_socket = 0;
65 
66 bool maclogger_enabled = false;
67 bool maclogger_exit = false;
68 
69 string maclogger_ip_address= "";;
70 string maclogger_ip_port= "";;
71 
72 string mclg_str = "";
73 int    mclg_rxhz;
74 int    mclg_txhz;
75 string mclg_band;
76 string mclg_mode;
77 string mclg_power;
78 string mclg_call;
79 string mclg_dxccnum;
80 string mclg_dxccstr;
81 string mclg_city;
82 string mclg_state;
83 string mclg_firstname;
84 string mclg_lastname;
85 string mclg_comment;
86 string mclg_bearing;
87 string mclg_longpath;
88 string mclg_distance;
89 
90 //======================================================================
91 // MacLogger UDP string parsing
92 //======================================================================
93 
get_str(string s)94 static string get_str(string s)
95 {
96 	size_t p = s.find(":");
97 	if (p == string::npos) return "";
98 	s.erase(0, p+1);
99 	p = s.find(",");
100 	if (p == string::npos) p = s.find("]");
101 	if (p == string::npos) return "";
102 	string s2 = s.substr(0, p);
103 	if (s2 == "(null)") return "";
104 	return s2;
105 }
106 
get_freq(string s)107 static int get_freq(string s)
108 {
109 	string s2 = get_str(s);
110 	size_t dpt = s2.find(".");
111 	if (dpt == string::npos) return 0;
112 	string sf = s2.substr(0, dpt);
113 	string sm = s2.substr(dpt+1);
114 	while(sm.length() < 6) sm.append("0");
115 	sf.append(sm);
116 	int fr = sf[0] - '0';
117 	for (size_t n = 1; n < sf.length(); n++) {
118 		fr *= 10;
119 		fr += (sf[n] - '0');
120 	}
121 //std::cout << "string: " << sf << ", int freq: " << fr << std::endl;
122 	return fr;
123 }
124 
maclogger_set_qsy()125 void maclogger_set_qsy()
126 {
127 	long hz = mclg_rxhz;
128 	if (hz <= 0 || !progdefaults.maclogger_spot_rx) hz = mclg_txhz;
129 	if (hz <= 0) return;
130 	sendFreq(hz);
131 	wf->rfcarrier(hz);
132 	wf->movetocenter();
133 	show_frequency(hz);
134 }
135 
maclogger_set_call()136 void maclogger_set_call()
137 {
138 	inpCall->value(mclg_call.c_str());
139 	inpCall->do_callback();
140 }
141 
maclogger_set_name()142 void maclogger_set_name()
143 {
144 	inpName->value(mclg_firstname.c_str());
145 	inpName->do_callback();
146 }
147 
maclogger_set_mode()148 void maclogger_set_mode()
149 {
150 //	inpMode->value(mclg_mode.c_str());
151 //	inpMode->do_callback();
152 }
153 
maclogger_set_qth()154 void maclogger_set_qth()
155 {
156 	inpQth->value(mclg_city.c_str());
157 	inpQth->do_callback();
158 }
159 
maclogger_set_state()160 void maclogger_set_state()
161 {
162 	inpState->value(mclg_state.c_str());
163 	inpState->do_callback();
164 }
165 
maclogger_disp_report(const char * s)166 void maclogger_disp_report(const char * s)
167 {
168 	txt_UDP_data->insert(s);
169 	txt_UDP_data->redraw();
170 }
171 
show_mac_strings()172 void show_mac_strings()
173 {
174 	SET_THREAD_ID(MACLOGGER_TID);
175 
176 	if (mclg_txhz > 0) REQ(maclogger_set_qsy);
177 	else if (mclg_rxhz > 0) REQ(maclogger_set_qsy);
178 	if (!mclg_mode.empty()) REQ(maclogger_set_mode);
179 	if (!mclg_call.empty()) REQ(maclogger_set_call);
180 	if (!mclg_city.empty()) REQ(maclogger_set_qth);
181 	if (!mclg_state.empty()) REQ(maclogger_set_state);
182 	if (!mclg_firstname.empty()) REQ(maclogger_set_name);
183 
184 //	if (!mclg_power.empty())
185 //	if (!mclg_band.empty())
186 //	if (!mclg_lastname.empty())
187 //	if (!mclg_comment.empty())
188 //	if (!mclg_bearing.empty())
189 //	if (!mclg_longpath.empty())
190 //	if (!mclg_distance.empty())
191 //	if (!mclg_dxccnum.empty())
192 //	if (!mclg_dxccstr.empty())
193 
194 }
195 
parse_report(string str)196 void parse_report(string str)
197 {
198 	size_t p;
199 	mclg_rxhz = 0;
200 	mclg_txhz = 0;
201 	mclg_band.clear();
202 	mclg_mode.clear();
203 	mclg_power.clear();
204 	mclg_call.clear();
205 	mclg_dxccnum.clear();
206 	mclg_dxccstr.clear();
207 	mclg_city.clear();
208 	mclg_state.clear();
209 	mclg_firstname.clear();
210 	mclg_lastname.clear();
211 	mclg_comment.clear();
212 	mclg_bearing.clear();
213 	mclg_longpath.clear();
214 	mclg_distance.clear();
215 
216 	if ((p = str.find("RxMHz:")) != string::npos)
217 		mclg_rxhz = get_freq(str.substr(p));
218 	if ((p = str.find("TxMHz:")) != string::npos)
219 		mclg_txhz = get_freq(str.substr(p));
220 	if ((p = str.find("Mode:"))  != string::npos)
221 		mclg_mode = get_str(str.substr(p));
222 	if ((p = str.find("Call:"))  != string::npos)
223 		mclg_call = get_str(str.substr(p));
224 	if ((p = str.find("city:")) != string::npos)
225 		mclg_city = get_str(str.substr(p));
226 	if ((p = str.find("state:")) != string::npos)
227 		mclg_state = get_str(str.substr(p));
228 	if ((p = str.find("first_name:")) != string::npos)
229 		mclg_firstname = get_str(str.substr(p));
230 
231 //	if ((p = mclg_str.find("dxcc_num:")) != string::npos)
232 //		mclg_dxccnum = get_str(mclg_str.substr(p));
233 //	if ((p = mclg_str.find("dxcc_string:")) != string::npos)
234 //		mclg_dxccstr = get_str(mclg_str.substr(p));
235 //	if ((p = mclg_str.find("Power:")) != string::npos)
236 //		mclg_power = get_str(mclg_str.substr(p));
237 //	if ((p = mclg_str.find("Band:"))  != string::npos)
238 //		mclg_band = get_str(mclg_str.substr(p));
239 //	if ((p = mclg_str.find("last_name:")) != string::npos)
240 //		mclg_lastname = get_str(mclg_str.substr(p));
241 //	if ((p = mclg_str.find("Comment:")) != string::npos)
242 //		mclg_comment = get_str(mclg_str.substr(p));
243 //	if ((p = mclg_str.find("Bearing:")) != string::npos)
244 //		mclg_bearing = get_str(mclg_str.substr(p));
245 //	if ((p = mclg_str.find("LongPath:")) != string::npos)
246 //		mclg_longpath = get_str(mclg_str.substr(p));
247 //	if ((p = mclg_str.find("Distance:")) != string::npos)
248 //		mclg_distance = get_str(mclg_str.substr(p));
249 
250 	show_mac_strings();
251 }
252 
parse_maclog()253 void parse_maclog()
254 {
255 	size_t p1, p2;
256 	string str;
257 	static string srep;
258 	while (!mclg_str.empty()) {
259 		p1 = mclg_str.find("[");
260 		if (p1 == string::npos) return;
261 		if (p1 != 0) mclg_str.erase(0, p1);
262 		p2 = mclg_str.find("]");
263 		if (p2 == string::npos) return;
264 
265 		str = mclg_str.substr(0, p2 + 1);
266 		srep = str;
267 		srep.append("\n");
268 		REQ(maclogger_disp_report, srep.c_str());
269 
270 		if (progdefaults.enable_maclogger_log) {
271 			std::string pathname = TempDir;
272 			pathname.append("maclogger_udp_strings.txt");
273 			FILE *maclog = fl_fopen(pathname.c_str(), "a");
274 			fprintf(maclog, "%s", srep.c_str());
275 			fclose(maclog);
276 		}
277 
278 		if ((progdefaults.capture_maclogger_radio &&
279 			 mclg_str.find("[Radio Report:") != std::string::npos)  ||
280 			(progdefaults.capture_maclogger_spot_tune &&
281 			 mclg_str.find("[SpotTune:") != std::string::npos) ||
282 			(progdefaults.capture_maclogger_spot_report &&
283 			 mclg_str.find("[Spot Report:") != std::string::npos) ||
284 			(progdefaults.capture_maclogger_log &&
285 			 mclg_str.find("[Log Report:") != std::string::npos) ||
286 			(progdefaults.capture_maclogger_lookup &&
287 			 mclg_str.find("[Lookup Report") != std::string::npos) )
288 			parse_report(str);
289 
290 		mclg_str.erase(0, p2 + 1);
291 
292 		MilliSleep(100);
293 	}
294 }
295 
296 //======================================================================
297 // uncomment to use UDP test strings
298 //
299 // #define TESTSTRINGS 1
300 //
301 //======================================================================
302 
303 #ifdef TESTSTRINGS
304 string tstring[6] = {
305 	"[Radio Report:RxMHz:24.96400, TxMHz:24.96400, Band:12M, Mode:USB, Power:5]",
306 	"[SpotTune:RxMHz:3.5095, TxMHz:3.549525, Band:10M, Mode:USB]",
307 	"[Log Report: Call:N2BJ, RxMHz:21.08580, TxMHz:21.08580, Band:15M, Mode:FSK, Power:5, dxcc_num:291, dxcc_string:United States, city:NEW LENOX, state:IL, first_name:Barry, last_name:COHEN]",
308 	"[Spot Report: RxMHz:3.50300, TxMHz:3.50300, Band:80M, Mode:CW, Call:EP6T, dxcc_string:Iran, Comment:UP , TNX CARLO , GL]",
309 	"[Rotor Report: Bearing:304.7, LongPath:0, Distance:0.0]",
310 	"[Lookup Report:Call:YC8RBI, RxMHz:21.32500, Band:15M, Mode:USB, dxcc_num:327, dxcc_string:Indonesia, Bearing:328.1, city:SANGIHE ISLAND NORTH SULAWESI, state:(null), first_name:RICHARD, last_name:BYL ( ICHA )]"
311 };
312 int tnbr = 0;
313 #endif
314 
get_maclogger_udp()315 void get_maclogger_udp()
316 {
317 #ifdef TESTSTRINGS
318 	if (tnbr == 0) {
319 		for (int n = 0; n < 6; n++) {
320 			mclg_str = tstring[n];
321 			parse_maclog();
322 		}
323 		tnbr = 1;
324 	}
325 #else
326 	if(!maclogger_socket) return;
327 	if (!progdefaults.connect_to_maclogger) return;
328 
329 	char buffer[MACLOGGER_BUFFER_SIZE];
330 	size_t count = 0;
331 
332 	memset(buffer, 0, sizeof(buffer));
333 
334 	try {
335 		count = maclogger_socket->recvFrom(
336 			(void *) buffer,
337 			sizeof(buffer) - 1);
338 	} catch (...) {
339 		LOG_WARN("MAC_logger socket error");
340 		count = 0;
341 	}
342 
343 	if (count) {
344 		mclg_str.append(buffer, count);
345 		parse_maclog();
346 	}
347 #endif
348 }
349 
350 //======================================================================
351 //
352 //======================================================================
maclogger_loop(void * args)353 void *maclogger_loop(void *args)
354 {
355 	SET_THREAD_ID(MACLOGGER_TID);
356 
357 	LOG_INFO("%s", "MAC_logger loop started. ");
358 
359 	while(1) {
360 		for (int i = 0; i < 100; i++) {
361 			MilliSleep(10);
362 			if (maclogger_exit) break;
363 		}
364 		if (maclogger_exit) break;
365 		get_maclogger_udp();
366 	}
367 	// exit the maclogger thread
368 	SET_THREAD_CANCEL();
369 	return NULL;
370 }
371 
372 //======================================================================
373 //
374 //======================================================================
maclogger_start(void)375 bool maclogger_start(void)
376 {
377 	maclogger_ip_address = "255.255.255.255";
378 	maclogger_ip_port = "9932";
379 
380 	try {
381 		maclogger_socket = new Socket(
382 				Address( maclogger_ip_address.c_str(),
383 						 maclogger_ip_port.c_str(),
384 						 "udp") );
385 		maclogger_socket->set_autoclose(true);
386 		maclogger_socket->set_nonblocking(false);
387 		maclogger_socket->bindUDP();
388 	}
389 	catch (const SocketException& e) {
390 		LOG_ERROR(
391 			"Could not resolve %s: %s",
392 			maclogger_ip_address.c_str(),
393 			e.what() );
394 		return false;
395 	}
396 
397 	return true;
398 }
399 
400 //======================================================================
401 //
402 //======================================================================
maclogger_init(void)403 void maclogger_init(void)
404 {
405 	maclogger_enabled = false;
406 	maclogger_exit = false;
407 
408 #ifndef TESTSTRINGS
409 	if(!maclogger_start()) return;
410 	LOG_INFO("%s", "UDP Init - OK");
411 #endif
412 
413 	if (pthread_create(&maclogger_thread, NULL, maclogger_loop, NULL) < 0) {
414 		LOG_ERROR("MACLOGGER maclogger_thread: pthread_create failed");
415 		return;
416 	}
417 
418 	LOG_INFO("MACLOGGER thread started");
419 
420 	maclogger_enabled = true;
421 }
422 
423 //======================================================================
424 //
425 //======================================================================
maclogger_close(void)426 void maclogger_close(void)
427 {
428 	if (!maclogger_enabled) return;
429 
430 	if(maclogger_socket) {
431 		maclogger_socket->shut_down();
432 		maclogger_socket->close();
433 	}
434 
435 	maclogger_exit = true;
436 	pthread_join(maclogger_thread, NULL);
437 
438 	LOG_INFO("%s", "MAC_logger loop terminated. ");
439 
440 	maclogger_enabled = false;
441 #ifdef TESTSTRINGS
442 	tnbr = 0;
443 #endif
444 }
445 
446