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