1 // ----------------------------------------------------------------------------
2 // psm/psm.cxx
3 //
4 // Support for Signal Montoring, CSMA, Transmit Inhibit (Busy Detection)
5 // When enabled effects all transmission types, Keybord, ARQ, and KISS.
6 //
7 // Copyright (c) 2016
8 //      Robert Stiles, KK5VD
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 "config.h"
27 
28 #ifdef __MINGW32__
29 #  include "compat.h"
30 #endif
31 
32 #include <fstream>
33 #include <sstream>
34 #include <string>
35 #include <cstdlib>
36 #include <ctime>
37 #include <errno.h>
38 #include <float.h>
39 #include <cstring>
40 
41 #include <sys/types.h>
42 #include <sys/time.h>
43 
44 #if !defined(__WOE32__) && !defined(__APPLE__)
45 #  include <sys/ipc.h>
46 #  include <sys/msg.h>
47 #endif
48 
49 #include <signal.h>
50 
51 #include <FL/Fl.H>
52 #include <FL/fl_ask.H>
53 #include <FL/Fl_Check_Button.H>
54 
55 #include "main.h"
56 #include "fl_digi.h"
57 #include "trx.h"
58 #include "globals.h"
59 #include "threads.h"
60 #include "socket.h"
61 #include "debug.h"
62 #include "qrunner.h"
63 #include "data_io.h"
64 #include "configuration.h"
65 #include "status.h"
66 #include "confdialog.h"
67 #include "psm/psm.h"
68 #include "gettext.h"
69 #include "timeops.h"
70 #include "kiss_io.h"
71 #include "xmlrpc.h"
72 #include "arq_io.h"
73 
74 #define HISTO_COUNT 256
75 static int HISTO_THRESH_HOLD = 48;
76 
77 // In seconds
78 #define HISTO_RESET_TIME 180
79 #define HISTO_RESET_TX_TIME_INHIBIT 3
80 
81 #define DISABLE_TX_INHIBIT_DURATION 5
82 #define EST_STATE_CHANGE_MS 25
83 
84 static int histogram[HISTO_COUNT];
85 //static bool init_hist_flag = true;
86 static double threshold = 5.0;
87 static int kpsql_pl = 0;
88 static double kpsql_threshold = 0.0;
89 time_t inhibit_tx_seconds = 0;
90 
91 // Used to scale the sensitivity of PSM
92 // Values range from 1/(largest int value) to 1/1
93 #define FGD_DEFAULT 2
94 static double fractional_gain = (1.0 / (1.0 * FGD_DEFAULT));
95 
96 static pthread_t       psm_pthread;
97 static pthread_cond_t  psm_cond;
98 static pthread_mutex_t psm_mutex;
99 
100 bool psm_thread_running   = false;
101 static bool psm_terminate_flag   = false;
102 static bool psm_thread_exit_flag = false;
103 
104 static bool request_transmit_flag = false;
105 
106 // A list of timers
107 static double timer_tramit_buffer_timeout = 0;
108 static double timer_slot_time = 0;
109 static double timer_inhibit_tx_seconds = 0;
110 static double timer_histrogram_reset_timer = 0;
111 static double timer_temp_disable_tx_inhibit = 0;
112 static double timer_sql_timer = 0;
113 
114 static pthread_mutex_t external_access_mutex = PTHREAD_MUTEX_INITIALIZER;
115 static pthread_mutex_t millisleep_mutex      = PTHREAD_MUTEX_INITIALIZER;
116 
117 static void update_sql_display(void);
118 static double detect_signal(int freq, int bw, double *low, double *high);
119 static void flush_tx_buffer(void);
120 static void process_psm(void);
121 static void * psm_loop(void *args);
122 static inline double current_double_time(void);
123 static void	psm_millisleep(int delay_time);
124 
125 bool csma_idling = 0;
126 
127 /**********************************************************************************
128  * Use a local version of MilliSleep()
129  **********************************************************************************/
psm_millisleep(int delay_time)130 static void	psm_millisleep(int delay_time)
131 {
132 	guard_lock _lock(&millisleep_mutex);
133 
134 	size_t seconds      = 0;
135 	size_t nano_seconds = 0;
136 
137 	struct timespec timeout = {0};
138 	double to_time = current_double_time();
139 
140 	to_time += (delay_time * 0.001);
141 
142 	seconds = (size_t) to_time;
143 	nano_seconds = (size_t) ((to_time - seconds) * 1000000000.0);
144 
145 	timeout.tv_sec  = seconds;
146 	timeout.tv_nsec = nano_seconds;
147 
148 	pthread_cond_timedwait(&psm_cond, &millisleep_mutex, &timeout);
149 }
150 
151 /**********************************************************************************
152  * Reset Histogram
153  **********************************************************************************/
psm_reset_histogram(void)154 void psm_reset_histogram(void)
155 {
156 	guard_lock _lock(&external_access_mutex);
157 
158 	memset(histogram, 0, sizeof(histogram));
159 	histogram[3] = 1;
160 	timer_inhibit_tx_seconds = current_double_time() + HISTO_RESET_TX_TIME_INHIBIT;
161 }
162 
163 /**********************************************************************************
164  *
165  **********************************************************************************/
update_kpsql_fractional_gain(int value)166 void update_kpsql_fractional_gain(int value)
167 {
168 	guard_lock _lock(&external_access_mutex);
169 
170 	if(value > 1) {
171 		progdefaults.kpsql_attenuation = value;
172 		fractional_gain = 1.0 / ((double) value);
173 	} else {
174 		progdefaults.kpsql_attenuation = FGD_DEFAULT;
175 		fractional_gain = 1.0 / ( 1.0 * FGD_DEFAULT);
176 	}
177 }
178 
179 /**********************************************************************************
180  *
181  **********************************************************************************/
update_sql_display(void)182 static void update_sql_display(void)
183 {
184 	static int prev_power_level = 0;
185 	static double convert_scale = (1.0 / ((double)HISTO_COUNT));
186 
187 	if(progdefaults.show_psm_btn && progStatus.kpsql_enabled) {
188 		double high_limit = 0;
189 		double low_limit = 0;
190 
191 		if(kpsql_pl != prev_power_level) {
192 			prev_power_level = kpsql_pl;
193 			high_limit = sldrSquelch->maximum();
194 			low_limit = sldrSquelch->minimum();
195 			if(kpsql_pl > HISTO_COUNT) {
196 				REQ(callback_set_metric, low_limit);
197 			} else {
198 				double diff = high_limit - low_limit;
199 				double scaled_value = kpsql_pl * convert_scale;
200 				double convert_value = scaled_value * diff;
201 				double results = high_limit - convert_value;
202 				REQ(callback_set_metric, results);
203 			}
204 		}
205 	}
206 }
207 
208 /**********************************************************************************
209  * To deal with the AGC from radios we create a ratio between
210  * the high and low signal levels.
211  **********************************************************************************/
detect_signal(int freq,int bw,double * low,double * high)212 static double detect_signal(int freq, int bw, double *low, double *high)
213 {
214 	int freq_step = 10;
215 	int freq_pos = 0;
216 	int start_freq = freq - (bw >> 1);
217 	int end_freq = freq + (bw >> 1);
218 	int freq_half_step = freq_step >> 1;
219 	int i = 0;
220 	double low_value = FLT_MAX;
221 	double high_value = FLT_MIN;
222 	double ratio = 0.0;
223 	double pd = 0;
224 	double ratio_avg = 0.0;
225 	static double pratio0 = 0.0;
226 	static double pratio1 = 0.0;
227 	static double pratio2 = 0.0;
228 	static double pratio3 = 0.0;
229 
230 	if(trx_state != STATE_RX) return ratio_avg;
231 
232 	for(i = 0; start_freq <= end_freq; start_freq += freq_step, i++) {
233 		freq_pos = start_freq + freq_half_step;
234 		pd = wf->powerDensity((double) freq_pos, (double) freq_step);
235 		if(pd < low_value)  low_value = pd;
236 		if(pd > high_value) high_value = pd;
237 	}
238 
239 	if(low)  *low = low_value;
240 	if(high) *high = high_value;
241 
242 	ratio = high_value/low_value;
243 	ratio *= fractional_gain;
244 
245 	kpsql_pl = ratio_avg = (ratio + pratio0 + pratio1 + pratio2 + pratio3) * 0.20;
246 
247 	if((ratio_avg > 0.0) && (ratio_avg <= (double) HISTO_COUNT)) {
248 		i = (int) ratio_avg;
249 		i &= 0xFF;
250 		histogram[i]++;
251 
252 		if(histogram[i] > HISTO_THRESH_HOLD) {
253 			for(i = 0; i < HISTO_COUNT; i++) {
254 				histogram[i] >>= 1;
255 			}
256 			return 0.0;
257 		}
258 	}
259 
260 	pratio3 = pratio2;
261 	pratio2 = pratio1;
262 	pratio1 = pratio0;
263 	pratio0 = ratio;
264 
265 	return ratio;
266 }
267 
268 /**********************************************************************************
269  * Clear all transmit buffers (ARQ/KISS/XMLRPC)
270  **********************************************************************************/
flush_tx_buffer(void)271 static void flush_tx_buffer(void)
272 {
273 	if(kiss_text_available) {
274 		flush_kiss_tx_buffer();
275 		kiss_text_available = false;
276 	}
277 
278 	if(arq_text_available) {
279 		flush_arq_tx_buffer();
280 		arq_text_available = false;
281 	}
282 
283 	if(xmltest_char_available) {
284 		reset_xmlchars();
285 		xmltest_char_available = false;
286 	}
287 }
288 
289 /**********************************************************************************
290  * Set state for PSM transmit.
291  **********************************************************************************/
psm_transmit(void)292 void psm_transmit(void)
293 {
294 	guard_lock extern_lock(&external_access_mutex);
295 	request_transmit_flag = true;
296 }
297 
298 /**********************************************************************************
299  * Clear state for PSM transmit.
300  **********************************************************************************/
psm_transmit_ended(int flag)301 void psm_transmit_ended(int flag)
302 {
303 	guard_lock extern_lock(&external_access_mutex);
304 
305 	if(flag == PSM_ABORT) {
306 		flush_tx_buffer();
307 		abort_tx();
308 	}
309 
310 	request_transmit_flag = false;
311 	REQ(set_xmtrcv_selection_color_transmitting);
312 }
313 
314 /**********************************************************************************
315  * Convert timespec difference to absolute double.
316  **********************************************************************************/
317 #if 0
318 static double timespec_difference(timespec * ts_a, timespec * ts_b)
319 {
320 	if(!ts_a) return 0.0;
321 	if(!ts_b) return 0.0;
322 
323 	double a = ts_a->tv_sec + (ts_a->tv_nsec * 0.000000001);
324 	double b = ts_b->tv_sec + (ts_b->tv_nsec * 0.000000001);
325 
326 	if(a > b)
327 		return (a - b);
328 
329 	return (b - a);
330 }
331 #endif // 0
332 
333 /**********************************************************************************
334  * Convert timespec to double.
335  **********************************************************************************/
current_double_time(void)336 static inline double current_double_time(void)
337 {
338 	struct timespec current_timespec_time = {0};
339 	clock_gettime(CLOCK_REALTIME, &current_timespec_time);
340 	double a = current_timespec_time.tv_sec + (current_timespec_time.tv_nsec * 0.000000001);
341 	return a;
342 }
343 
344 /**********************************************************************************
345  * PSM processing. Sync's with Waterfall Display Update
346  **********************************************************************************/
process_psm(void)347 static void process_psm(void)
348 {
349 	if (!progdefaults.show_psm_btn) return;
350 	if (!progStatus.kpsql_enabled)  return;
351 
352 	guard_lock psm_lock(&psm_mutex);
353 
354 	bool   detected_signal = false;
355 	bool   transmit_authorized = true;
356 
357 	double busyChannelSeconds = 0;
358 	double current_time = 0;
359 	double level = 0.0;
360 	double random_number = 0;
361 
362 	int    bw = active_modem->get_bandwidth();
363 	int    bw_margin = progStatus.psm_minimum_bandwidth_margin;
364 	int    freq = active_modem->get_txfreq();
365 
366 	static bool   histrogram_reset_timer = true;
367 	static bool   signal_recorded_flag   = false;
368 	static double signal_hit_time        = 0;
369 	static int    delay_time             = 0;
370 
371 	current_time = current_double_time();
372 
373 	level = detect_signal(freq, bw + bw_margin, 0, 0);
374 
375 	if(!progStatus.enableBusyChannel) {
376 		timer_inhibit_tx_seconds = temp_disable_tx_inhibit = 0;
377 	}
378 
379 	// Enabled on valid packet reception. Currently only available
380 	// to checksum verified protocols (HDLC).
381 
382 	if(temp_disable_tx_inhibit) {
383 		timer_temp_disable_tx_inhibit = current_time + DISABLE_TX_INHIBIT_DURATION;
384 		temp_disable_tx_inhibit = 0;
385 	}
386 
387 	random_number = (rand() & 0xFF) * 0.00390625; // Reduce value to 0 - 1.0
388 
389 	if(current_time < timer_temp_disable_tx_inhibit) {
390 		busyChannelSeconds = 0.25 + (random_number * 0.75); // 0.25 - 1.0 Seconds
391 	} else {
392 		busyChannelSeconds = (double) progStatus.busyChannelSeconds + random_number;
393 	}
394 
395 	if(timer_tramit_buffer_timeout == 0.0) {
396 		timer_tramit_buffer_timeout = current_time + (progStatus.psm_flush_buffer_timeout * 60); // Minutes to Seconds
397 	}
398 
399 	// If busy for an extended time flush transmit buffer(s).
400 	if(progStatus.psm_flush_buffer_timeout) { // If set to zero no buffer flushing allowed.
401 		if(current_time > timer_tramit_buffer_timeout) {
402 			timer_tramit_buffer_timeout = current_time + (progStatus.psm_flush_buffer_timeout * 60);
403 			flush_tx_buffer();
404 			return;
405 		}
406 	}
407 
408 	if(histrogram_reset_timer) {
409 		timer_histrogram_reset_timer = current_time + HISTO_RESET_TIME;
410 		histrogram_reset_timer = false;
411 	}
412 
413 	if(current_time > timer_histrogram_reset_timer) {
414 		psm_reset_histogram();
415 		timer_histrogram_reset_timer = current_time + HISTO_RESET_TIME;
416 		timer_inhibit_tx_seconds = current_time + 2.0;  // Time to rebuild the histogram table.
417 		return;
418 	}
419 
420 	// Histogram keeps the threshold 'x' number of units above the noise level.
421 	if(progStatus.psm_use_histogram) {
422 		int idx = 0;
423 		int first_value = 0;
424 		int offset = progStatus.psm_histogram_offset_threshold;
425 		int index = 0;
426 		if(offset > HISTO_COUNT) offset = HISTO_COUNT;
427 
428 		for(index = 0; index < HISTO_COUNT; index++) {
429 			if(histogram[index]) {
430 				if(idx == 0) {
431 					first_value = index;
432 				}
433 
434 				if(idx >= offset) {
435 					threshold = (double) index;
436 					break;
437 				}
438 				idx++;
439 			}
440 		}
441 
442 		if(index > HISTO_COUNT) {
443 			threshold = (double) (first_value + offset);
444 		}
445 	} else {
446 		threshold = (int) (progStatus.sldrPwrSquelchValue * 2.56); // Histogram scaled.
447 	}
448 
449 	kpsql_threshold = threshold;
450 
451 	if(level < threshold) {
452 		detected_signal      = false;
453 		signal_recorded_flag = false;
454 	}
455 	else {
456 		detected_signal = true;
457 		if(!signal_recorded_flag) {
458 			signal_hit_time = current_double_time();
459 			signal_recorded_flag = true;
460 		}
461 	}
462 
463 	if(progStatus.enableBusyChannel && detected_signal) {
464 		double signal_hit_time_test = (progStatus.psm_hit_time_window * 0.001);  // Milliseconds to seconds.
465 		double signal_hit_time_diff = (current_time - signal_hit_time);
466 		if(signal_hit_time_diff >= signal_hit_time_test) {
467 			timer_inhibit_tx_seconds = current_time + busyChannelSeconds;
468 		}
469 	}
470 
471 	if(current_time < timer_inhibit_tx_seconds) {
472 		inhibit_tx_seconds = true;
473 	} else {
474 		inhibit_tx_seconds = false;
475 	}
476 
477 	// Limit the number of times update_sql_display() is called per second.
478 	if(current_time > timer_sql_timer) {
479 		update_sql_display();
480 		timer_sql_timer = current_time + 0.06; // Eyeball tested value.
481 	}
482 
483 	if(inhibit_tx_seconds || !request_transmit_flag ||
484 	   detected_signal || (current_time < timer_slot_time))
485 		return;
486 
487 	delay_time = 0;
488 
489 	if(progStatus.csma_enabled) {
490 
491 		int rn_persistance = rand() & 0xFF;
492 
493 		if(rn_persistance > progStatus.csma_persistance) {
494 			double _slot_time = ((progdefaults.csma_slot_time * 10) * 0.001);
495 			timer_slot_time = current_time + _slot_time;
496 			transmit_authorized = false;
497 		}
498 
499 		if(progStatus.csma_transmit_delay > 0) {
500 			csma_idling = true;
501 			delay_time = progStatus.csma_transmit_delay * 10;
502 		}
503 	}
504 
505 	if(transmit_authorized && (trx_state == STATE_RX)) {
506 
507 		REQ(set_xmtrcv_selection_color_transmitting);
508 
509 		trx_transmit_psm();
510 		active_modem->set_stopflag(false);
511 
512 		// Transmit idle time plus START_RX to STATE_TX state change
513 		// delay.
514 		if(delay_time > 0) {
515 			psm_millisleep(delay_time + EST_STATE_CHANGE_MS);
516 			delay_time = 0;
517 			csma_idling = false;
518 		} else {
519 			psm_millisleep(EST_STATE_CHANGE_MS);
520 		}
521 
522 		timer_tramit_buffer_timeout = current_time + (progStatus.psm_flush_buffer_timeout * 60);
523 		timer_slot_time = current_time + ((progdefaults.csma_slot_time * 10) * 0.001);
524 		timer_slot_time += (((rand() & 0xFF) * 0.00390625) * 0.20);
525 	}
526 }
527 
528 /**********************************************************************************
529  * PSM processing loop. Sync's with Waterfall Display Update
530  **********************************************************************************/
psm_loop(void * args)531 static void * psm_loop(void *args)
532 {
533 	SET_THREAD_ID(PSM_TID);
534 
535 	psm_thread_running   = true;
536 	psm_terminate_flag   = false;
537 	psm_thread_exit_flag = false;
538 
539 	while(1) {
540 		pthread_mutex_lock(&psm_mutex);
541 		pthread_cond_wait(&psm_cond, &psm_mutex);
542 		pthread_mutex_unlock(&psm_mutex);
543 
544 		if (psm_terminate_flag) break;
545 
546 		if(trx_state == STATE_RX) {
547 			process_psm();
548 		}
549 	}
550 
551 	psm_thread_exit_flag = true;
552 
553 	return (void *)0;
554 }
555 
556 /**********************************************************************************
557  * Start PSM Thread
558  **********************************************************************************/
start_psm_thread(void)559 void start_psm_thread(void)
560 {
561 	guard_lock extern_lock(&external_access_mutex);
562 	csma_idling = false;
563 
564 	if(psm_thread_running) return;
565 
566 	memset((void *) &psm_pthread, 0, sizeof(psm_pthread));
567 	memset((void *) &psm_cond,    0, sizeof(psm_cond));
568 	memset((void *) &psm_mutex,   0, sizeof(psm_mutex));
569 
570 	if(pthread_cond_init(&psm_cond, NULL)) {
571 		LOG_ERROR("PSM thread create fail (pthread_cond_init)");
572 		return;
573 	}
574 
575 	if(pthread_mutex_init(&psm_mutex, NULL)) {
576 		LOG_ERROR("PSM thread create fail (pthread_mutex_init)");
577 		pthread_cond_destroy(&psm_cond);
578 		return;
579 	}
580 
581 	memset((void *) &psm_pthread, 0, sizeof(psm_pthread));
582 
583 	if(!psm_thread_running) {
584 		if (pthread_create(&psm_pthread, NULL, psm_loop, NULL) < 0) {
585 			pthread_cond_destroy(&psm_cond);
586 			pthread_mutex_destroy(&psm_mutex);
587 			LOG_ERROR("PSM thread create fail (pthread_create)");
588 		}
589 	}
590 
591 	MilliSleep(10); // Give the CPU time to set 'psm_thread_running'
592 }
593 
594 /**********************************************************************************
595  * Stop PSM Thread
596  **********************************************************************************/
stop_psm_thread(void)597 void stop_psm_thread(void)
598 {
599 	guard_lock extern_lock(&external_access_mutex);
600 
601 	if(!psm_thread_running) return;
602 
603 	psm_terminate_flag = true;
604 	pthread_cond_signal(&psm_cond);
605 
606 	MilliSleep(10);
607 
608 	if(psm_thread_exit_flag) {
609 		pthread_join(psm_pthread, NULL);
610 		LOG_INFO("%s", "psm thread - join");
611 	} else {
612 		CANCEL_THREAD(psm_pthread);
613 		LOG_INFO("%s", "psm thread - cancel");
614 	}
615 
616 	pthread_cond_destroy(&psm_cond);
617 	pthread_mutex_destroy(&psm_mutex);
618 
619 	memset((void *) &psm_pthread, 0, sizeof(psm_pthread));
620 	memset((void *) &psm_cond,    0, sizeof(psm_cond));
621 	memset((void *) &psm_mutex,   0, sizeof(psm_mutex));
622 
623 	psm_thread_running   = false;
624 	psm_terminate_flag   = false;
625 	psm_thread_exit_flag = false;
626 	csma_idling          = false;
627 
628 }
629 
630 /**********************************************************************************
631  * Signal PSM to process Waterfall power level information.
632  **********************************************************************************/
signal_psm(void)633 void signal_psm(void)
634 {
635 	if(psm_thread_running) {
636 		pthread_cond_signal(&psm_cond);
637 	}
638 }
639