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, ¤t_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