1 // ----------------------------------------------------------------------------
2 // trx.cxx -- Main transmit/receive control loop / thread
3 //
4 // Copyright (C) 2006-2010
5 // Dave Freese, W1HKJ
6 // Copyright (C) 2007-2010
7 // Stelios Bounanos, M0GLD
8 //
9 // This file is part of fldigi. Adapted in part from code contained in gmfsk
10 // source code distribution.
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 #include <sys/time.h>
29 #include <fcntl.h>
30 #include <semaphore.h>
31 #include <cstdlib>
32 #include <string>
33 #include <string.h>
34
35 #include "trx.h"
36 #include "main.h"
37 #include "fl_digi.h"
38 #include "ascii.h"
39 #include "misc.h"
40 #include "configuration.h"
41 #include "status.h"
42 #include "dtmf.h"
43
44 #include "soundconf.h"
45 #include "ringbuffer.h"
46 #include "qrunner.h"
47 #include "debug.h"
48 #include "nullmodem.h"
49 #include "macros.h"
50 #include "rigsupport.h"
51 #include "psm/psm.h"
52 #include "icons.h"
53 #include "fft-monitor.h"
54 #include "audio_alert.h"
55
56 extern fftmon *fft_modem;
57
58 #include "spectrum_viewer.h"
59
60 #if BENCHMARK_MODE
61 # include "benchmark.h"
62 #endif
63
64 LOG_FILE_SOURCE(debug::LOG_MODEM);
65
66 using namespace std;
67
68 void trx_reset_loop();
69 void trx_start_modem_loop();
70 void trx_receive_loop();
71 void trx_transmit_loop();
72 void trx_tune_loop();
73 static void trx_signal_state(void);
74
75 //#define DEBUG
76
77 /* ---------------------------------------------------------------------- */
78
79 static sem_t* trx_sem;
80 static pthread_t trx_thread;
81 state_t trx_state;
82
83 modem *active_modem = 0;
84 cRsId *ReedSolomon = 0;
85 cDTMF *dtmf = 0;
86 SoundBase *RXscard = 0;
87 bool RXsc_is_open = false;
88 bool TXsc_is_open = false;
89 SoundBase *TXscard = 0;
90 static int current_RXsamplerate = 0;
91 static int current_TXsamplerate = 0;
92
93 static int _trx_tune;
94
95 // Ringbuffer for the audio "history". A pointer into this buffer
96 // is also passed to the waterfall signal drawing routines.
97 #define NUMMEMBUFS 1024
98 static ringbuffer<double> trxrb(ceil2(NUMMEMBUFS * SCBLOCKSIZE));
99 static float fbuf[SCBLOCKSIZE];
100 bool bHistory = false;
101 bool bHighSpeed = false;
102 static double hsbuff[SCBLOCKSIZE];
103
104 static bool trxrunning = false;
105
106 extern bool trx_inhibit;
107 bool rx_only = false;
108
109 #include "tune.cxx"
110
111 //=============================================================================
112
113 // Draws the xmit data one WF_BLOCKSIZE-sized block at a time
trx_xmit_wfall_draw(int samplerate)114 static void trx_xmit_wfall_draw(int samplerate)
115 {
116 ENSURE_THREAD(TRX_TID);
117
118 ringbuffer<double>::vector_type rv[2];
119 rv[0].buf = 0;
120 rv[1].buf = 0;
121
122 #define block_read_(vec_) \
123 while (vec_.len >= WF_BLOCKSIZE) { \
124 wf->sig_data(vec_.buf, WF_BLOCKSIZE); \
125 REQ(&waterfall::handle_sig_data, wf); \
126 vec_.len -= WF_BLOCKSIZE; \
127 vec_.buf += WF_BLOCKSIZE; \
128 trxrb.read_advance(WF_BLOCKSIZE); \
129 }
130
131 trxrb.get_rv(rv);
132 block_read_(rv[0]); // read blocks from the first vector
133
134 if (rv[0].len + rv[1].len < WF_BLOCKSIZE)
135 return;
136 if (rv[0].len == 0)
137 block_read_(rv[1]);
138 #undef block_read_
139
140 // read non-contiguous data into tmp buffer so that we can
141 // still draw it one block at a time
142 if (unlikely(trxrb.read_space() >= WF_BLOCKSIZE)) {
143 double buf[WF_BLOCKSIZE];
144 do {
145 trxrb.read(buf, WF_BLOCKSIZE);
146 wf->sig_data(buf, WF_BLOCKSIZE);
147 REQ(&waterfall::handle_sig_data, wf);
148 } while (trxrb.read_space() >= WF_BLOCKSIZE);
149 }
150 }
151
152 // Called by trx_trx_transmit_loop() to handle data that may be left in the
153 // ringbuffer when we stop transmitting. Will pad with zeroes to a multiple of
154 // WF_BLOCKSIZE.
trx_xmit_wfall_end(int samplerate)155 static void trx_xmit_wfall_end(int samplerate)
156 {
157 ENSURE_THREAD(TRX_TID);
158
159 size_t pad = WF_BLOCKSIZE - trxrb.read_space() % WF_BLOCKSIZE;
160 if (pad == WF_BLOCKSIZE) // rb empty or multiple of WF_BLOCKSIZE
161 return;
162
163 ringbuffer<double>::vector_type wv[2];
164 wv[0].buf = wv[1].buf = 0;
165
166 trxrb.get_wv(wv, pad);
167 assert(wv[0].len + wv[1].len == pad);
168
169 if (likely(wv[0].len)) { // fill first vector, write rest to second vector
170 memset(wv[0].buf, 0, wv[0].len * sizeof(*wv[0].buf));
171 if (pad > wv[0].len)
172 memset(wv[1].buf, 0, (pad - wv[0].len) * sizeof(*wv[1].buf));
173 }
174 else // all write space is in the second write vector
175 memset(wv[1].buf, 0, pad * sizeof(*wv[1].buf));
176
177 trxrb.write_advance(pad);
178
179 trx_xmit_wfall_draw(samplerate);
180 }
181
182 // Copy buf to the ringbuffer if it has enough space. Queue a waterfall
183 // request whenever there are at least WF_BLOCKSIZE samples to draw.
trx_xmit_wfall_queue(int samplerate,const double * buf,size_t len)184 void trx_xmit_wfall_queue(int samplerate, const double* buf, size_t len)
185 {
186 ENSURE_THREAD(TRX_TID);
187 ringbuffer<double>::vector_type wv[2];
188 wv[0].buf = wv[1].buf = 0;
189
190 trxrb.get_wv(wv, len);
191 if (unlikely(wv[0].len + wv[1].len < len)) // not enough space
192 return;
193
194 size_t n = MIN(wv[0].len, len);
195
196 for (size_t i = 0; i < n; i++)
197 wv[0].buf[i] = buf[i] * progdefaults.TxMonitorLevel;
198
199 if (len > n) { // write the remainder to the second vector
200 buf += n;
201 n = len - n;
202 for (size_t i = 0; i < n; i++)
203 wv[1].buf[i] = buf[i] * progdefaults.TxMonitorLevel;
204
205 }
206
207 trxrb.write_advance(len);
208 if (trxrb.read_space() >= WF_BLOCKSIZE)
209 trx_xmit_wfall_draw(samplerate);
210 }
211
212 //=============================================================================
213
audio_select_failure(std::string errmsg)214 void audio_select_failure(std::string errmsg)
215 {
216 progdefaults.btnAudioIOis = SND_IDX_NULL; // file i/o
217 sound_update(progdefaults.btnAudioIOis);
218 btnAudioIO[0]->value(0);
219 btnAudioIO[1]->value(0);
220 btnAudioIO[2]->value(0);
221 btnAudioIO[3]->value(1);
222 delete RXscard;
223 RXscard = 0;
224 fl_alert2("Could not open audio device: %s\nCheck for h/w connection, and restart fldigi", errmsg.c_str());
225 }
226
trx_trx_receive_loop()227 void trx_trx_receive_loop()
228 {
229 size_t numread;
230 assert(powerof2(SCBLOCKSIZE));
231
232 if (unlikely(!active_modem)) {
233 MilliSleep(10);
234 return;
235 }
236
237 #if BENCHMARK_MODE
238 do_benchmark();
239 trx_state = STATE_ENDED;
240 return;
241 #endif
242
243 if (!RXscard) {
244 MilliSleep(10);
245 return;
246 }
247
248 try {
249 if (!progdefaults.is_full_duplex || !RXsc_is_open ||
250 current_RXsamplerate != active_modem->get_samplerate() ) {
251 current_RXsamplerate = active_modem->get_samplerate();
252 if (RXscard) {
253 RXscard->Close(O_RDONLY);
254 RXscard->Open(O_RDONLY, current_RXsamplerate);
255 REQ(sound_update, progdefaults.btnAudioIOis);
256 RXsc_is_open = true;
257 }
258 }
259 }
260 catch (const SndException& e) {
261 LOG_ERROR("%s. line: %i", e.what(), __LINE__);
262 put_status(e.what(), 5);
263 if (RXscard) RXscard->Close();
264 RXsc_is_open = false;
265 current_RXsamplerate = 0;
266 if (progdefaults.btnAudioIOis == SND_IDX_PORT) {
267 sound_close();
268 sound_init();
269 }
270 REQ(audio_select_failure, e.what());
271 MilliSleep(100);
272 return;
273 }
274 active_modem->rx_init();
275
276 ringbuffer<double>::vector_type rbvec[2];
277 rbvec[0].buf = rbvec[1].buf = 0;
278
279 if (RXscard) RXscard->flush(O_RDONLY);
280
281 while (1) {
282 try {
283 numread = 0;
284 if (current_RXsamplerate != active_modem->get_samplerate() ) {
285 current_RXsamplerate = active_modem->get_samplerate();
286 if (RXscard) {
287 RXscard->Close(O_RDONLY);
288 RXscard->Open(O_RDONLY, current_RXsamplerate);
289 REQ(sound_update, progdefaults.btnAudioIOis);
290 RXsc_is_open = true;
291 }
292 }
293 if (RXscard) {
294 while (numread < SCBLOCKSIZE && trx_state == STATE_RX)
295 numread += RXscard->Read(fbuf + numread, SCBLOCKSIZE - numread);
296 }
297 if (numread > SCBLOCKSIZE) {
298 LOG_ERROR("numread error %lu", (unsigned long) numread);
299 numread = SCBLOCKSIZE;
300 }
301 if (bHighSpeed) {
302 for (size_t i = 0; i < numread; i++)
303 hsbuff[i] = fbuf[i];
304 } else {
305 if (trxrb.write_space() == 0) // diRXscard some old data
306 trxrb.read_advance(SCBLOCKSIZE);
307
308 size_t room = trxrb.get_wv(rbvec, numread);
309
310 if (room < numread) {
311 LOG_ERROR("trxrb.get_wv(rbvec) = %d, numread = %d", (int)room, (int)numread);
312 } else {
313 // convert to double and write to rb
314 for (size_t i = 0; i < numread; i++)
315 rbvec[0].buf[i] = fbuf[i];
316 }
317 }
318 }
319 catch (const SndException& e) {
320 if (RXscard) RXscard->Close();
321 RXsc_is_open = false;
322 LOG_ERROR("%s. line: %i", e.what(), __LINE__);
323 put_status(e.what(), 5);
324 MilliSleep(10);
325 return;
326 }
327 if (trx_state != STATE_RX)
328 break;
329
330 if (bHighSpeed) {
331 bool afc = progStatus.afconoff;
332 progStatus.afconoff = false;
333 QRUNNER_DROP(true);
334 if (progdefaults.rsid)
335 ReedSolomon->receive(fbuf, numread);
336 else if (active_modem->get_mode() == MODE_OFDM_500F || active_modem->get_mode() == MODE_OFDM_750F || active_modem->get_mode() == MODE_OFDM_2000F)
337 ReedSolomon->receive(fbuf, numread); // OFDM modes use RSID as AFC mechanism. Force RxRSID.
338 active_modem->HistoryON(true);
339 active_modem->rx_process(hsbuff, numread);
340 QRUNNER_DROP(false);
341 progStatus.afconoff = afc;
342 active_modem->HistoryON(false);
343 } else {
344 trxrb.write_advance(numread);
345
346 wf->sig_data(rbvec[0].buf, numread);
347
348 if (!trx_inhibit)
349 REQ(&waterfall::handle_sig_data, wf);
350 if (!bHistory) {
351 if (fft_modem && spectrum_viewer->visible())
352 fft_modem->rx_process(rbvec[0].buf, numread);
353 active_modem->rx_process(rbvec[0].buf, numread);
354
355 if (audio_alert)
356 audio_alert->monitor(rbvec[0].buf, numread, current_RXsamplerate);
357
358 if (progdefaults.rsid)
359 ReedSolomon->receive(fbuf, numread);
360 else if (active_modem->get_mode() == MODE_OFDM_500F || active_modem->get_mode() == MODE_OFDM_750F || active_modem->get_mode() == MODE_OFDM_2000F)
361 ReedSolomon->receive(fbuf, numread); // OFDM modes use RSID as AFC mechanism. Force RxRSID.
362 dtmf->receive(fbuf, numread);
363 } else {
364 bool afc = progStatus.afconoff;
365 progStatus.afconoff = false;
366 QRUNNER_DROP(true);
367 active_modem->HistoryON(true);
368 trxrb.get_rv(rbvec);
369 if (rbvec[0].len)
370 active_modem->rx_process(rbvec[0].buf, rbvec[0].len);
371 if (rbvec[1].len)
372 active_modem->rx_process(rbvec[1].buf, rbvec[1].len);
373 QRUNNER_DROP(false);
374 progStatus.afconoff = afc;
375 bHistory = false;
376 active_modem->HistoryON(false);
377 }
378 }
379 }
380 if (trx_state == STATE_RESTART)
381 return;
382
383 if (!progdefaults.is_full_duplex ) {
384 if (RXscard) RXscard->Close(O_RDONLY);
385 RXsc_is_open = false;
386 }
387 }
388
389
390 //=============================================================================
trx_trx_transmit_loop()391 void trx_trx_transmit_loop()
392 {
393 if (rx_only) return;
394
395 if (!TXscard) {
396 MilliSleep(10);
397 return;
398 }
399 if (active_modem) {
400
401 try {
402 if (current_TXsamplerate != active_modem->get_samplerate() || !TXsc_is_open) {
403 current_TXsamplerate = active_modem->get_samplerate();
404 if (TXscard) {
405 TXscard->Close(O_WRONLY);
406 TXscard->Open(O_WRONLY, current_TXsamplerate);
407 TXsc_is_open = true;
408 }
409 }
410 } catch (const SndException& e) {
411 LOG_ERROR("%s. line: %i", e.what(), __LINE__);
412 put_status(e.what(), 1);
413 current_TXsamplerate = 0;
414 MilliSleep(10);
415 return;
416 }
417
418 if ((active_modem != ssb_modem) &&
419 (active_modem != anal_modem) &&
420 !active_modem->XMLRPC_CPS_TEST &&
421 !PERFORM_CPS_TEST ) {
422 push2talk->set(true);
423 REQ(&waterfall::set_XmtRcvBtn, wf, true);
424 }
425 active_modem->tx_init();
426
427 bool _txrsid = false;
428 if ( ReedSolomon->assigned(active_modem->get_mode()) && (progdefaults.TransmitRSid || progStatus.n_rsids != 0))
429 _txrsid = true;
430 else if (ReedSolomon->assigned(active_modem->get_mode()) && (active_modem->get_mode() == MODE_OFDM_500F || active_modem->get_mode() == MODE_OFDM_750F || active_modem->get_mode() == MODE_OFDM_2000F) )
431 _txrsid = true; // RSID is used as header/preamble for OFDM modes. Make mandatory.
432
433 if (_txrsid) {
434 if (progStatus.n_rsids < 0) {
435 for (int i = 0; i > progStatus.n_rsids; i--) {
436 ReedSolomon->send(true);
437 MilliSleep(200);
438 }
439 } else if ( progStatus.n_rsids > 0 ) {
440 for (int i = 0; i < progStatus.n_rsids; i++) {
441 ReedSolomon->send(true);
442 MilliSleep(200);
443 }
444 MilliSleep(200);
445 if (progStatus.n_rsids == 1) progStatus.n_rsids = 0;
446 } else
447 ReedSolomon->send(true);
448 }
449
450 if (progStatus.n_rsids >= 0) {
451
452 active_modem->tx_sample_count = 0;
453 active_modem->tx_sample_rate = active_modem->get_samplerate();
454
455 while (trx_state == STATE_TX) {
456 try {
457 if (!progdefaults.DTMFstr.empty())
458 dtmf->send();
459 if (active_modem->tx_process() < 0) {
460 active_modem->cwid();
461 if (trx_state != STATE_ABORT)
462 trx_state = STATE_RX;
463 }
464 }
465 catch (const SndException& e) {
466 if (TXscard) TXscard->Close();
467 TXsc_is_open = false;
468 LOG_ERROR("%s", e.what());
469 put_status(e.what(), 5);
470 current_TXsamplerate = 0;
471 MilliSleep(10);
472 return;
473 }
474 }
475 } else
476 if (trx_state != STATE_ABORT && trx_state != STATE_RESTART)
477 trx_state = STATE_RX;
478
479 if (ReedSolomon->assigned(active_modem->get_mode()) &&
480 progdefaults.TransmitRSid &&
481 progdefaults.rsid_post &&
482 progStatus.n_rsids >= 0) ReedSolomon->send(false);
483
484 progStatus.n_rsids = 0;
485
486 trx_xmit_wfall_end(current_TXsamplerate);
487
488 if (TXscard) TXscard->flush();
489
490 if (trx_state == STATE_RX) {
491 if (!progdefaults.is_full_duplex) {
492 if (TXscard) TXscard->Close(O_WRONLY);
493 TXsc_is_open = false;
494 }
495 }
496
497 } else
498 MilliSleep(10);
499
500 push2talk->set(false);
501 REQ(&waterfall::set_XmtRcvBtn, wf, false);
502 psm_transmit_ended(PSM_STOP);
503 if (progStatus.timer)
504 REQ(startMacroTimer);
505 WriteARQ(0x06);
506 }
507
508 //=============================================================================
trx_tune_loop()509 void trx_tune_loop()
510 {
511 if (rx_only) return;
512
513 if (!TXscard) {
514 MilliSleep(10);
515 return;
516 }
517 if (active_modem) {
518 try {
519 if (!progdefaults.is_full_duplex || !TXsc_is_open ||
520 current_TXsamplerate != active_modem->get_samplerate() ) {
521 current_TXsamplerate = active_modem->get_samplerate();
522 if (TXscard) TXscard->Close(O_WRONLY);
523 if (TXscard) {
524 TXscard->Open(O_WRONLY, current_TXsamplerate);
525 TXsc_is_open = true;
526 }
527 }
528 } catch (const SndException& e) {
529 LOG_ERROR("%s. line: %i", e.what(), __LINE__);
530 put_status(e.what(), 1);
531 MilliSleep(10);
532 current_TXsamplerate = 0;
533 return;
534 }
535
536 push2talk->set(true);
537 active_modem->tx_init();
538
539 try {
540 if (active_modem->get_mode() == MODE_CW &&
541 (use_nanoIO ||
542 progStatus.WK_online ||
543 progdefaults.CW_KEYLINE_on_cat_port ||
544 CW_KEYLINE_isopen)) {
545 if (CW_KEYLINE_isopen || progdefaults.CW_KEYLINE_on_cat_port)
546 active_modem->CW_KEYLINE(1);
547 else if (use_nanoIO)
548 nanoCW_tune(1);
549 else WK_tune(1);
550 cwio_ptt(1);
551 cwio_key(1);
552
553 REQ(&waterfall::set_XmtRcvBtn, wf, true);
554 while (trx_state == STATE_TUNE) MilliSleep(10);
555
556 if (CW_KEYLINE_isopen) active_modem->CW_KEYLINE(0);
557 else if (use_nanoIO) nanoCW_tune(0);
558 else WK_tune(0);
559 cwio_key(0);
560 cwio_ptt(0);
561 } else {
562 while (trx_state == STATE_TUNE) {
563 if (_trx_tune == 0) {
564 REQ(&waterfall::set_XmtRcvBtn, wf, true);
565 xmttune::keydown(active_modem->get_txfreq_woffset(), TXscard);
566 _trx_tune = 1;
567 } else
568 xmttune::tune(active_modem->get_txfreq_woffset(), TXscard);
569 }
570 xmttune::keyup(active_modem->get_txfreq_woffset(), TXscard);
571 }
572 }
573 catch (const SndException& e) {
574 if (TXscard) TXscard->Close();
575 TXsc_is_open = false;
576 LOG_ERROR("%s. line: %i", e.what(), __LINE__);
577 put_status(e.what(), 5);
578 MilliSleep(10);
579 current_TXsamplerate = 0;
580 return;
581 }
582 if (TXscard) TXscard->flush();
583
584 if (trx_state == STATE_RX) {
585 if (!progdefaults.is_full_duplex) {
586 if (TXscard) TXscard->Close(O_WRONLY);
587 TXsc_is_open = false;
588 }
589 }
590
591 _trx_tune = 0;
592 } else
593 MilliSleep(10);
594
595 push2talk->set(false);
596 REQ(&waterfall::set_XmtRcvBtn, wf, false);
597 }
598
599 //=============================================================================
trx_loop(void * args)600 void *trx_loop(void *args)
601 {
602 SET_THREAD_ID(TRX_TID);
603
604 state_t old_state = STATE_NOOP;
605
606 for (;;) {
607 if (unlikely(old_state != trx_state)) {
608 old_state = trx_state;
609 if (trx_state == STATE_TX || trx_state == STATE_TUNE)
610 trxrb.reset();
611 trx_signal_state();
612 }
613
614 LOG_DEBUG("trx state %s",
615 trx_state == STATE_ABORT ? "abort" :
616 trx_state == STATE_ENDED ? "ended" :
617 trx_state == STATE_RESTART ? "restart" :
618 trx_state == STATE_NEW_MODEM ? "new modem" :
619 trx_state == STATE_TX ? "tx" :
620 trx_state == STATE_TUNE ? "tune" :
621 trx_state == STATE_RX ? "rx" :
622 "unknown");
623
624 switch (trx_state) {
625 case STATE_ABORT:
626 delete RXscard;
627 RXscard = 0;
628 delete TXscard;
629 TXscard = 0;
630 trx_state = STATE_ENDED;
631 // fall through
632 case STATE_ENDED:
633 REQ(set_flrig_ptt, 0);
634 stop_deadman();
635 return 0;
636 case STATE_RESTART:
637 REQ(set_flrig_ptt, 0);
638 stop_deadman();
639 trx_reset_loop();
640 break;
641 case STATE_NEW_MODEM:
642 REQ(set_flrig_ptt, 0);
643 trx_start_modem_loop();
644 break;
645 case STATE_TX:
646 REQ(set_flrig_ptt, 1);
647 start_deadman();
648 trx_trx_transmit_loop();
649 break;
650 case STATE_TUNE:
651 REQ(set_flrig_ptt, 1);
652 start_deadman();
653 trx_tune_loop();
654 break;
655 case STATE_RX:
656 REQ(set_flrig_ptt, 0);
657 stop_deadman();
658 trx_trx_receive_loop();
659 break;
660 default:
661 LOG(debug::ERROR_LEVEL, debug::LOG_MODEM, "trx in bad state %d\n", trx_state);
662 MilliSleep(100);
663 }
664 }
665 }
666
667 //=============================================================================
668 static modem* new_modem;
669 static int new_freq;
670
trx_start_modem_loop()671 void trx_start_modem_loop()
672 {
673 if (new_modem == active_modem) {
674 if (new_freq > 0 && !progdefaults.retain_freq_lock)
675 active_modem->set_freq(new_freq);
676 else if (new_freq > 0 && (active_modem->get_mode() == MODE_OFDM_500F || active_modem->get_mode() == MODE_OFDM_750F || active_modem->get_mode() == MODE_OFDM_2000F || active_modem->get_mode() == MODE_OFDM_2000))
677 active_modem->set_freq(new_freq); // OFDM modes use RSID as AFC mechanism. Always allow QSY of Rx Frequency.
678 active_modem->restart();
679 trx_state = STATE_RX;
680 if (progdefaults.show_psm_btn &&
681 progStatus.kpsql_enabled &&
682 progStatus.psm_use_histogram)
683 psm_reset_histogram();
684 return;
685 }
686
687 modem* old_modem = active_modem;
688
689 new_modem->init();
690 active_modem = new_modem;
691 if (new_freq > 0 && !progdefaults.retain_freq_lock)
692 active_modem->set_freq(new_freq);
693 else if (new_freq > 0 && (active_modem->get_mode() == MODE_OFDM_500F || active_modem->get_mode() == MODE_OFDM_750F || active_modem->get_mode() == MODE_OFDM_2000F || active_modem->get_mode() == MODE_OFDM_2000))
694 active_modem->set_freq(new_freq); // OFDM modes use RSID as AFC mechanism. Always allow QSY of Rx Frequency.
695 trx_state = STATE_RX;
696 REQ(&waterfall::opmode, wf);
697 REQ(set599);
698
699 if (old_modem) {
700 *mode_info[old_modem->get_mode()].modem = 0;
701 delete old_modem;
702 }
703 }
704
705 //=============================================================================
trx_start_modem(modem * m,int f)706 void trx_start_modem(modem* m, int f)
707 {
708 new_modem = m;
709 new_freq = f;
710 trx_state = STATE_NEW_MODEM;
711 }
712
713 //=============================================================================
714 static string reset_loop_msg;
show_reset_loop_alert()715 void show_reset_loop_alert()
716 {
717 // if (btnAudioIO[0]) {
718 btnAudioIO[0]->value(0);
719 btnAudioIO[1]->value(0);
720 btnAudioIO[2]->value(0);
721 btnAudioIO[3]->value(1);
722 fl_alert2("%s", reset_loop_msg.c_str());
723 // }
724 }
725
trx_reset_loop()726 void trx_reset_loop()
727 {
728 if (RXscard) {
729 RXscard->Close();
730 RXsc_is_open = false;
731 delete RXscard;
732 RXscard = 0;
733 }
734 if (TXscard) {
735 TXscard->Close();
736 TXsc_is_open = false;
737 delete TXscard;
738 TXscard = 0;
739 }
740
741 switch (progdefaults.btnAudioIOis) {
742 #if USE_OSS
743 case SND_IDX_OSS:
744 try {
745 RXscard = new SoundOSS(scDevice[0].c_str());
746 if (!RXscard) break;
747
748 RXscard->Open(O_RDONLY, current_RXsamplerate = 8000);
749 RXsc_is_open = true;
750
751 TXscard = new SoundOSS(scDevice[0].c_str());
752 if (!TXscard) break;
753
754 TXscard->Open(O_WRONLY, current_TXsamplerate = 8000);
755 TXsc_is_open = true;
756 } catch (...) {
757 reset_loop_msg = "OSS open failure";
758 progdefaults.btnAudioIOis = SND_IDX_NULL; // file i/o
759 sound_update(progdefaults.btnAudioIOis);
760 REQ(show_reset_loop_alert);
761 }
762 break;
763 #endif
764 #if USE_PORTAUDIO
765 /// All of this very convoluted logic is needed to allow a Linux user
766 /// to switch from PulseAudio to PortAudio. PulseAudio does not immediately
767 /// release the sound card resources after closing the pulse audio object.
768 case SND_IDX_PORT:
769 {
770 RXscard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
771 TXscard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
772 unsigned long tm1 = zmsec();
773 int RXret = 0, TXret = 0;
774 int i;
775 RXsc_is_open = false;
776 TXsc_is_open = false;
777 for (i = 0; i < 10; i++) { // try 10 times
778 try {
779 if (!RXret)
780 RXret = RXscard->Open(O_RDONLY, current_RXsamplerate = 8000);
781 if (progdefaults.is_full_duplex) {
782 if (!TXret)
783 TXret = TXscard->Open(O_WRONLY, current_TXsamplerate = 8000);
784 }
785 if (RXret) RXsc_is_open = true;
786 if (TXret) TXsc_is_open = true;
787 break;
788 } catch (const SndException& e) {
789 MilliSleep(50);
790 Fl::awake();
791 }
792 }
793 unsigned long tm = zmsec() - tm1;
794 if (tm < 0) tm = 0;
795 if (i == 10) {
796 if (RXscard) delete RXscard;
797 if (TXscard) delete TXscard;
798 RXscard = 0;
799 TXscard = 0;
800 LOG_PERROR("Port Audio device not available");
801 reset_loop_msg = "Port Audio device not available";
802 progdefaults.btnAudioIOis = SND_IDX_NULL; // file i/o
803 sound_update(progdefaults.btnAudioIOis);
804 REQ(show_reset_loop_alert);
805 } else {
806 LOG_INFO ("Port Audio device available after %0.1f seconds", tm / 1000.0 );
807 }
808 break;
809 }
810 #endif
811 #if USE_PULSEAUDIO
812 case SND_IDX_PULSE:
813 try {
814 RXscard = new SoundPulse(scDevice[0].c_str());
815 if (!RXscard) break;
816
817 RXscard->Open(O_RDONLY, current_RXsamplerate = 8000);
818 RXsc_is_open = true;
819
820 TXscard = new SoundPulse(scDevice[0].c_str());
821 if (!TXscard) break;
822
823 // needed to open playback device in PaVolumeControl
824 TXscard->Open(O_WRONLY, current_TXsamplerate = 8000);
825 double buffer[1024];
826 for (int i = 0; i < 1024; buffer[i++] = 0);
827 TXscard->Write_stereo(buffer, buffer, 1024);
828
829 if (progdefaults.is_full_duplex)
830 TXsc_is_open = true;
831 else {
832 TXscard->Close();
833 TXsc_is_open = false;
834 }
835 } catch (const SndException& e) {
836 LOG_ERROR("%s", e.what());
837 if (RXscard) delete RXscard;
838 if (TXscard) delete TXscard;
839 RXscard = 0;
840 TXscard = 0;
841 reset_loop_msg = "Pulse Audio error:\n";
842 reset_loop_msg.append(e.what());
843 reset_loop_msg.append("\n\nIs the server running?\nClose fldigi and execute 'pulseaudio --start'");
844 progdefaults.btnAudioIOis = SND_IDX_NULL; // file i/o
845 sound_update(progdefaults.btnAudioIOis);
846 REQ(show_reset_loop_alert);
847 }
848 break;
849 #endif
850 case SND_IDX_NULL:
851 RXscard = new SoundNull;
852 TXscard = new SoundNull;
853 current_RXsamplerate = current_TXsamplerate = 0;
854 break;
855 default:
856 abort();
857 }
858
859 trx_state = STATE_RX;
860 }
861
862 //=============================================================================
863
trx_reset(void)864 void trx_reset(void)
865 {
866 trx_state = STATE_RESTART;
867 }
868
869 //=============================================================================
870
trx_start(void)871 void trx_start(void)
872 {
873 #if !BENCHMARK_MODE
874 if (trxrunning) {
875 LOG(debug::ERROR_LEVEL, debug::LOG_MODEM, "trx already running!");
876 return;
877 }
878
879 if (RXscard) {
880 delete RXscard;
881 RXscard = 0;
882 }
883 if (TXscard) {
884 delete TXscard;
885 TXscard = 0;
886 }
887 if (ReedSolomon) delete ReedSolomon;
888 if (dtmf) delete dtmf;
889
890
891 switch (progdefaults.btnAudioIOis) {
892 #if USE_OSS
893 case SND_IDX_OSS:
894 RXscard = new SoundOSS(scDevice[0].c_str());
895 TXscard = new SoundOSS(scDevice[0].c_str());
896 break;
897 #endif
898 #if USE_PORTAUDIO
899 case SND_IDX_PORT:
900 RXscard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
901 TXscard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
902 break;
903 #endif
904 #if USE_PULSEAUDIO
905 case SND_IDX_PULSE:
906 try {
907 RXscard = new SoundPulse(scDevice[0].c_str());
908 if (!RXscard) break;
909
910 TXscard = new SoundPulse(scDevice[0].c_str());
911 if (!TXscard) break;
912
913 // needed to open playback device in PaVolumeControl
914 TXscard->Open(O_WRONLY, current_TXsamplerate = 8000);
915 double buffer[1024];
916 for (int i = 0; i < 1024; buffer[i++] = 0);
917 TXscard->Write_stereo(buffer, buffer, 1024);
918
919 if (progdefaults.is_full_duplex)
920 TXsc_is_open = true;
921 else {
922 TXscard->Close();
923 TXsc_is_open = false;
924 }
925 } catch (const SndException& e) {
926 LOG_ERROR("%s", e.what());
927 if (RXscard) delete RXscard;
928 if (TXscard) delete TXscard;
929 RXscard = 0;
930 TXscard = 0;
931 reset_loop_msg = "Pulse Audio error:\n";
932 reset_loop_msg.append(e.what());
933 reset_loop_msg.append("\n\nIs the server running?");
934 progdefaults.btnAudioIOis = SND_IDX_NULL; // file i/o
935 sound_update(progdefaults.btnAudioIOis);
936 REQ(show_reset_loop_alert);
937 }
938 break;
939 #endif
940 case SND_IDX_NULL:
941 RXscard = new SoundNull;
942 TXscard = new SoundNull;
943 break;
944 default:
945 abort();
946 }
947 current_RXsamplerate = current_TXsamplerate = 0;
948
949 ReedSolomon = new cRsId;
950 dtmf = new cDTMF;
951
952 #endif // !BENCHMARK_MODE
953
954 #if USE_NAMED_SEMAPHORES
955 char sname[32];
956 snprintf(sname, sizeof(sname), "trx-%u-%s", getpid(), PACKAGE_TARNAME);
957 if ((trx_sem = sem_open(sname, O_CREAT | O_EXCL, 0600, 0)) == (sem_t*)SEM_FAILED) {
958 LOG_PERROR("sem_open");
959 abort();
960 }
961 # if HAVE_SEM_UNLINK
962 if (sem_unlink(sname) == -1) {
963 LOG_PERROR("sem_unlink");
964 abort();
965 }
966 # endif
967 #else
968 trx_sem = new sem_t;
969 if (sem_init(trx_sem, 0, 0) == -1) {
970 LOG_PERROR("sem_init");
971 abort();
972 }
973 #endif
974
975 trx_state = STATE_RX;
976 _trx_tune = 0;
977 active_modem = 0;
978 if (pthread_create(&trx_thread, NULL, trx_loop, NULL) < 0) {
979 LOG(debug::ERROR_LEVEL, debug::LOG_MODEM, "pthread_create failed");
980 trxrunning = false;
981 exit(1);
982 }
983 trxrunning = true;
984 }
985
986 //=============================================================================
trx_close()987 void trx_close()
988 {
989 LOG_INFO("%s", "closing trx thread");
990 int count = 1000;
991 active_modem->set_stopflag(true);
992 while (trx_state != STATE_RX && count--)
993 MilliSleep(10);
994 if (trx_state != STATE_RX) {
995 LOG_INFO("%s", "trx_state != STATE_RX");
996 exit(1);
997 }
998 count = 1000;
999 trx_state = STATE_ABORT;
1000 while (trx_state != STATE_ENDED && count--)
1001 MilliSleep(10);
1002 if (trx_state != STATE_ENDED) {
1003 LOG_INFO("%s", "trx_state != STATE_ENDED");
1004 exit(2);
1005 }
1006 #if USE_NAMED_SEMAPHORES
1007 if (sem_close(trx_sem) == -1)
1008 LOG_PERROR("sem_close");
1009 #else
1010 if (sem_destroy(trx_sem) == -1)
1011 LOG_PERROR("sem_destroy");
1012 delete trx_sem;
1013 #endif
1014
1015 if (RXscard) {
1016 delete RXscard;
1017 RXscard = 0;
1018 }
1019 LOG_INFO("%s", "trx thread closed");
1020 }
1021
1022 //=============================================================================
trx_transmit_psm(void)1023 void trx_transmit_psm(void) { trx_state = STATE_TX; };
1024
trx_transmit(void)1025 void trx_transmit(void) {
1026 if (progdefaults.show_psm_btn &&
1027 progStatus.kpsql_enabled &&
1028 (!PERFORM_CPS_TEST || !active_modem->XMLRPC_CPS_TEST))
1029 psm_transmit();
1030 else
1031 trx_state = STATE_TX;
1032 }
1033
trx_tune(void)1034 void trx_tune(void) { trx_state = STATE_TUNE; }
trx_receive(void)1035 void trx_receive(void) { trx_state = STATE_RX; }
1036
1037 //=============================================================================
1038
trx_wait_state(void)1039 void trx_wait_state(void)
1040 {
1041 ENSURE_NOT_THREAD(TRX_TID);
1042 sem_wait(trx_sem);
1043 }
1044
trx_signal_state(void)1045 static void trx_signal_state(void)
1046 {
1047 ENSURE_THREAD(TRX_TID);
1048 sem_post(trx_sem);
1049 }
1050