1 // ----------------------------------------------------------------------------
2 // hamlib.cxx -- Hamlib (rig control) interface for fldigi
3 //
4 // Copyright (C) 2007-2009
5 // Dave Freese, W1HKJ
6 // Copyright (C) 2008-2009
7 // Stelios Bounanos, M0GLD
8 //
9 // This file is part of fldigi.
10 //
11 // Fldigi is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // Fldigi is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24
25 #include <config.h>
26
27 #include <cstdlib>
28 #include <string>
29 #include <vector>
30 #include <algorithm>
31
32 #include "trx.h"
33 #include "configuration.h"
34 #include "confdialog.h"
35
36 #include "rigclass.h"
37
38 #include "threads.h"
39 #include "misc.h"
40
41 #include "fl_digi.h"
42 #include "main.h"
43 #include "misc.h"
44
45 #include "rigsupport.h"
46
47 #include "stacktrace.h"
48 #ifdef __WOE32__
49 # include "serial.h"
50 #endif
51 #include "debug.h"
52 #include "re.h"
53
54 LOG_FILE_SOURCE(debug::LOG_RIGCONTROL);
55
56 using namespace std;
57
58 static pthread_mutex_t hamlib_mutex = PTHREAD_MUTEX_INITIALIZER;
59 static pthread_t *hamlib_thread = 0;
60
61 static bool hamlib_exit = false;
62
63 static bool hamlib_ptt = false;
64 static bool hamlib_qsy = false;
65 static bool need_freq = false;
66 static bool need_mode = false;
67 static bool hamlib_bypass = false;
68 static bool hamlib_closed = true;//false;
69 static int hamlib_passes = 20;
70
71 static long int hamlib_freq;
72 static rmode_t hamlib_rmode = RIG_MODE_USB;
73 static pbwidth_t hamlib_pbwidth = 3000;
74
75 typedef std::vector<const struct rig_caps *> rig_list_t;
76 rig_list_t hamlib_rigs;
77
78 enum { SIDEBAND_RIG, SIDEBAND_LSB, SIDEBAND_USB };
79
80 static void *hamlib_loop(void *args);
81
show_error(const char * msg1,const char * msg2=0)82 void show_error(const char* msg1, const char* msg2 = 0)
83 {
84 string error = msg1;
85 if (msg2)
86 error.append(": ").append(msg2);
87 put_status(error.c_str(), 10.0);
88 LOG_ERROR("%s", error.c_str());
89 }
90
hamlib_get_defaults()91 void hamlib_get_defaults()
92 {
93 char szParam[40];
94 int i;
95 Rig testrig;
96 rig_model_t rigmodel;
97
98 rigmodel = hamlib_get_rig_model(cboHamlibRig->index());
99 testrig.init(rigmodel);
100
101 testrig.getConf("timeout", szParam);
102 sscanf(szParam, "%d", &i);
103 cntHamlibTimeout->value(i);
104 LOG_VERBOSE("Hamlib default Timeout: %d", i);
105
106 testrig.getConf("retry", szParam);
107 sscanf(szParam, "%d", &i);
108 cntHamlibRetries->value(i);
109 LOG_VERBOSE("Hamlib default Retry: %d", i);
110
111 testrig.getConf("post_write_delay", szParam);
112 sscanf(szParam, "%d", &i);
113 cntHamlibWait->value(i);
114 LOG_VERBOSE("Hamlib default Post write delay: %d", i);
115
116 testrig.getConf("write_delay", szParam);
117 sscanf(szParam, "%d", &i);
118 cntHamlibWriteDelay->value(i);
119 LOG_VERBOSE("Hamlib default Write delay: %d", i);
120
121 if (testrig.getCaps()->port_type == RIG_PORT_SERIAL) {
122
123 testrig.getConf("serial_speed", szParam);
124 listbox_baudrate->value(szParam);
125 LOG_VERBOSE("Hamlib default serial speed: %s", szParam);
126
127 testrig.getConf("rts_state", szParam);
128 chkHamlibRTSplus->value( strcmp(szParam, "ON") == 0 ? true : false);
129 LOG_VERBOSE("Hamlib default RTS state: %s", szParam);
130
131 testrig.getConf("dtr_state", szParam);
132 btnHamlibDTRplus->value( strcmp(szParam, "ON") == 0 ? true : false);
133 LOG_VERBOSE("Hamlib default DTR state: %s", szParam);
134
135 testrig.getConf("serial_handshake", szParam);
136 chkHamlibRTSCTSflow->value(strcmp(szParam, "Hardware") == 0 ? true : false);
137 chkHamlibXONXOFFflow->value(strcmp(szParam, "XONXOFF") == 0 ? true : false);
138 LOG_VERBOSE("Hamlib default serial handshake: %s", szParam);
139
140 testrig.getConf("stop_bits", szParam);
141 valHamRigStopbits->value(strcmp(szParam, "1") == 0 ? 1 : 2);
142 LOG_VERBOSE("Hamlib default stop bits: %s", szParam);
143 }
144
145 if (!testrig.canSetPTT()) {
146 btnHamlibCMDptt->value(0);
147 btnHamlibCMDptt->deactivate();
148 } else {
149 btnHamlibCMDptt->value(1);
150 btnHamlibCMDptt->activate();
151 }
152
153 inpHamlibConfig->value("");
154
155 testrig.close();
156 }
157
hamlib_init_defaults()158 void hamlib_init_defaults()
159 {
160 progdefaults.HamRigModel = hamlib_get_rig_model(cboHamlibRig->index());
161 progdefaults.HamRigDevice = inpRIGdev->value();
162 progdefaults.HamlibRetries = static_cast<int>(cntHamlibRetries->value());
163 progdefaults.HamlibTimeout = static_cast<int>(cntHamlibTimeout->value());
164 progdefaults.HamlibWriteDelay = static_cast<int>(cntHamlibWriteDelay->value());
165 progdefaults.HamlibWait = static_cast<int>(cntHamlibWait->value());
166 progdefaults.HamlibCMDptt = btnHamlibCMDptt->value();
167 progdefaults.HamlibDTRplus = btnHamlibDTRplus->value();
168 progdefaults.HamlibRTSCTSflow = chkHamlibRTSCTSflow->value();
169 progdefaults.HamlibRTSplus = chkHamlibRTSplus->value();
170 progdefaults.HamlibXONXOFFflow = chkHamlibXONXOFFflow->value();
171 progdefaults.HamlibSideband = listbox_sideband->index();
172 progdefaults.HamRigStopbits = static_cast<int>(valHamRigStopbits->value());
173 progdefaults.HamRigBaudrate = listbox_baudrate->index();
174 progdefaults.HamlibCMDptt = btnHamlibCMDptt->value();
175 progdefaults.HamConfig = inpHamlibConfig->value();
176 }
177
hamlib_init(bool bPtt)178 bool hamlib_init(bool bPtt)
179 {
180 freq_t freq;
181
182 hamlib_ptt = bPtt;
183
184 hamlib_init_defaults();
185
186 #ifdef __CYGWIN__
187 string port = progdefaults.HamRigDevice;
188 com_to_tty(port);
189 #endif
190
191 if (progdefaults.HamRigModel == 0) {
192 LOG_ERROR("No such hamlib rig model");
193 return false;
194 }
195
196 try {
197 char szParam[20];
198
199 xcvr->init(progdefaults.HamRigModel);
200
201 #ifdef __CYGWIN__
202 xcvr->setConf("rig_pathname", port.c_str());
203 #else
204 xcvr->setConf("rig_pathname", progdefaults.HamRigDevice.c_str());
205 #endif
206
207 snprintf(szParam, sizeof(szParam), "%d", progdefaults.HamlibWait);
208 xcvr->setConf("post_write_delay", szParam);
209
210 snprintf(szParam, sizeof(szParam), "%d", progdefaults.HamlibWriteDelay);
211 xcvr->setConf("write_delay", szParam);
212
213 snprintf(szParam, sizeof(szParam), "%d", progdefaults.HamlibTimeout);
214 xcvr->setConf("timeout", szParam);
215
216 snprintf(szParam, sizeof(szParam), "%d", progdefaults.HamlibRetries);
217 xcvr->setConf("retry", szParam);
218
219 if (xcvr->getCaps()->port_type == RIG_PORT_SERIAL) {
220 xcvr->setConf("serial_speed", progdefaults.strBaudRate());
221 if (progdefaults.HamlibDTRplus)
222 xcvr->setConf("dtr_state", "ON");
223 else
224 xcvr->setConf("dtr_state", "OFF");
225 if (progdefaults.HamlibRTSCTSflow) {
226 xcvr->setConf("serial_handshake", "Hardware");
227 } else if (progdefaults.HamlibXONXOFFflow) {
228 xcvr->setConf("serial_handshake", "XONXOFF");
229 } else {
230 xcvr->setConf("serial_handshake", "None");
231 }
232
233 if (!progdefaults.HamlibRTSCTSflow) {
234 if (progdefaults.HamlibRTSplus)
235 xcvr->setConf("rts_state", "ON");
236 else
237 xcvr->setConf("rts_state", "OFF");
238 }
239 xcvr->setConf("stop_bits", progdefaults.HamRigStopbits == 1 ? "1" : "2");
240 }
241
242 string::size_type c = progdefaults.HamConfig.find('#');
243 if (c != string::npos)
244 progdefaults.HamConfig.erase(c);
245 if (!progdefaults.HamConfig.empty()) {
246 re_t re("([^, =]+) *= *([^, =]+)", REG_EXTENDED);
247 const char* conf = progdefaults.HamConfig.c_str();
248 int end;
249 while (re.match(conf)) {
250 xcvr->setConf(re.submatch(1).c_str(), re.submatch(2).c_str());
251 re.suboff(0, NULL, &end);
252 conf += end;
253 }
254 }
255 xcvr->open();
256 }
257 catch (const RigException& Ex) {
258 show_error(__func__, Ex.what());
259 xcvr->close();
260 return false;
261 }
262
263 try {
264 if ( !xcvr->canGetFreq() ) need_freq = false; // getFreq will return setFreq value
265 else {
266 need_freq = true;
267 freq = xcvr->getFreq(RIG_VFO_A);
268 if ((long)freq < 0) { //<= 0) {
269 xcvr->close(true);
270 LOG_ERROR("%s","Hamlib xcvr not responding");
271 return false;
272 }
273 }
274 }
275 catch (const RigException& Ex) {
276 show_error("Get Freq", Ex.what());
277 need_freq = false;
278 }
279 if (!need_freq) {
280 xcvr->close(true);
281 LOG_INFO("Failed freq test");
282 return false;
283 }
284
285 LOG_INFO("trying mode request");
286 try {
287 need_mode = xcvr->canGetMode();
288 }
289 catch (const RigException& Ex) {
290 LOG_ERROR("Get Mode %s", Ex.what());
291 need_mode = false;
292 }
293
294 try {
295 if (hamlib_ptt == true) {
296 LOG_INFO("trying PTT");
297 if (!xcvr->canSetPTT())
298 hamlib_ptt = false;
299 else
300 xcvr->setPTT(RIG_PTT_OFF);
301 }
302 }
303 catch (const RigException& Ex) {
304 LOG_ERROR("Set Ptt %s", Ex.what());
305 hamlib_ptt = false;
306 }
307
308 hamlib_freq = 0;
309 hamlib_rmode = RIG_MODE_NONE;
310
311 hamlib_exit = false;
312 hamlib_bypass = false;
313
314 hamlib_thread = new pthread_t;
315
316 if (pthread_create(hamlib_thread, NULL, hamlib_loop, NULL) < 0) {
317 show_error(__func__, "pthread_create failed");
318 xcvr->close();
319 hamlib_thread = 0;
320 return false;
321 }
322
323 init_Hamlib_RigDialog();
324
325 hamlib_closed = false;
326 return true;
327 }
328
hamlib_close(void)329 void hamlib_close(void)
330 {
331 ENSURE_THREAD(FLMAIN_TID);
332
333 if (hamlib_closed)
334 return;
335
336 pthread_mutex_lock(&hamlib_mutex);
337 hamlib_exit = true;
338 pthread_mutex_unlock(&hamlib_mutex);
339
340 pthread_join(*hamlib_thread, NULL);
341 delete hamlib_thread;
342 hamlib_thread = 0;
343
344 if (xcvr->isOnLine()) xcvr->close();
345 wf->USB(true);
346
347 }
348
hamlib_active(void)349 bool hamlib_active(void)
350 {
351 if (!xcvr) return false;
352 return (xcvr->isOnLine());
353 }
354
hamlib_set_ptt(int ptt)355 void hamlib_set_ptt(int ptt)
356 {
357 if (xcvr->isOnLine() == false)
358 return;
359 if (!hamlib_ptt)
360 return;
361 guard_lock hamlib(&hamlib_mutex);
362 try {
363 xcvr->setPTT( ptt ?
364 (progdefaults.hamlib_ptt_on_data ? RIG_PTT_ON_DATA : RIG_PTT_ON_MIC) :
365 RIG_PTT_OFF );
366 hamlib_bypass = ptt ? true : false;
367 }
368 catch (const RigException& Ex) {
369 show_error("Rig PTT", Ex.what());
370 hamlib_ptt = false;
371 }
372 }
373
hamlib_set_qsy(long long f)374 void hamlib_set_qsy(long long f)
375 {
376 if (xcvr->isOnLine() == false)
377 return;
378 guard_lock hamlib(&hamlib_mutex);
379 double fdbl = f;
380 hamlib_qsy = false;
381 try {
382 xcvr->setFreq(fdbl);
383 wf->rfcarrier(f);
384 wf->movetocenter();
385 }
386 catch (const RigException& Ex) {
387 show_error("QSY", Ex.what());
388 hamlib_passes = 0;
389 }
390 }
391
hamlib_setfreq(long f)392 int hamlib_setfreq(long f)
393 {
394 if (xcvr->isOnLine() == false)
395 return -1;
396 guard_lock hamlib(&hamlib_mutex);
397 try {
398 LOG_DEBUG("%ld", f);
399 xcvr->setFreq(f);
400 }
401 catch (const RigException& Ex) {
402 show_error("SetFreq", Ex.what());
403 hamlib_passes = 0;
404 }
405 return 1;
406 }
407
408 static int hamlib_wait = 0;
409
hamlib_setmode(rmode_t m)410 int hamlib_setmode(rmode_t m)
411 {
412 if (need_mode == false)
413 return -1;
414 if (xcvr->isOnLine() == false)
415 return -1;
416 guard_lock hamlib(&hamlib_mutex);
417 try {
418 hamlib_rmode = xcvr->getMode(hamlib_pbwidth);
419 xcvr->setMode(m, hamlib_pbwidth);
420 hamlib_rmode = m;
421 }
422 catch (const RigException& Ex) {
423 show_error("Set Mode", Ex.what());
424 hamlib_passes = 0;
425 }
426 hamlib_wait = progdefaults.hamlib_mode_delay / 50;
427 return 1;
428 }
429
430 // width control via hamlib is not implemented
431
hamlib_setwidth(pbwidth_t w)432 int hamlib_setwidth(pbwidth_t w)
433 {
434 if (xcvr->isOnLine() == false)
435 return -1;
436 guard_lock hamlib(&hamlib_mutex);
437 try {
438 hamlib_rmode = xcvr->getMode(hamlib_pbwidth);
439 xcvr->setMode(hamlib_rmode, w);
440 hamlib_pbwidth = w;
441 }
442 catch (const RigException& Ex) {
443 show_error("Set Width", Ex.what());
444 hamlib_passes = 0;
445 }
446 return 1;
447 }
448
hamlib_getmode()449 rmode_t hamlib_getmode()
450 {
451 return hamlib_rmode;
452 }
453
hamlib_getwidth()454 pbwidth_t hamlib_getwidth()
455 {
456 return hamlib_pbwidth;
457 }
458
hamlib_USB()459 bool hamlib_USB()
460 {
461 if (hamlib_wait) return wf->USB();
462 bool islsb = false;
463 if (progdefaults.HamlibSideband == SIDEBAND_RIG) {
464 islsb = (hamlib_rmode == RIG_MODE_LSB ||
465 hamlib_rmode == RIG_MODE_PKTLSB ||
466 hamlib_rmode == RIG_MODE_ECSSLSB);
467 if (hamlib_rmode == RIG_MODE_CW) {
468 if (progdefaults.hamlib_cw_islsb) islsb = true;
469 else islsb = false;
470 }
471 if (hamlib_rmode == RIG_MODE_CWR) {
472 if (progdefaults.hamlib_cw_islsb) islsb = false;
473 else islsb = true;
474 }
475 if (hamlib_rmode == RIG_MODE_RTTY) {
476 if (progdefaults.hamlib_rtty_isusb) islsb = false;
477 else islsb = true;
478 }
479 if (hamlib_rmode == RIG_MODE_RTTYR) {
480 if (progdefaults.hamlib_rtty_isusb) islsb = true;
481 else islsb = false;
482 }
483 } else if (progdefaults.HamlibSideband == SIDEBAND_LSB)
484 islsb = true;
485 return !islsb;
486 }
487
hamlib_loop(void * args)488 static void *hamlib_loop(void *args)
489 {
490 SET_THREAD_ID(RIGCTL_TID);
491
492 long int freq = 0L;
493 rmode_t numode = RIG_MODE_NONE;
494 int skips = 0;
495
496 for (;;) {
497 bool cont = false;
498 MilliSleep(50);
499
500 if (skips) {
501 skips--;
502 cont = true;
503 } if (hamlib_wait) {
504 hamlib_wait--;
505 cont = true;
506 }
507
508 if (cont)
509 continue;
510 else {
511 skips = valHamRigPollrate->value() / 50;
512 }
513
514
515 if (hamlib_exit)
516 break;
517 if (hamlib_bypass)
518 continue;
519
520 {
521 guard_lock hamlib(&hamlib_mutex);
522 if (need_freq) {
523 freq_t f;
524 try {
525 f = xcvr->getFreq();
526 freq = (long int) f;
527 if (freq == 0) continue;
528 hamlib_freq = freq;
529 show_frequency(hamlib_freq);
530 wf->rfcarrier(hamlib_freq);
531 }
532 catch (const RigException& Ex) {
533 show_error(__func__, "Rig not responding: freq");
534 }
535 }
536 }
537 if (hamlib_exit)
538 break;
539 if (hamlib_bypass)
540 continue;
541
542 {
543 guard_lock hamlib(&hamlib_mutex);
544 if (need_mode) {
545 try {
546 numode = xcvr->getMode(hamlib_pbwidth);
547 if (numode != hamlib_rmode) {
548 hamlib_rmode = numode;
549 show_mode(modeString(hamlib_rmode));
550 wf->USB(hamlib_USB());
551 }
552 }
553 catch (const RigException& Ex) {
554 show_error(__func__, "Rig not responding: mode");
555 }
556 }
557 }
558
559 if (hamlib_exit)
560 break;
561 if (hamlib_bypass)
562 continue;
563
564 }
565
566 hamlib_closed = true;
567
568 return NULL;
569 }
570
add_to_list(const struct rig_caps * rc,void *)571 static int add_to_list(const struct rig_caps* rc, void*)
572 {
573 hamlib_rigs.push_back(rc);
574 return 1;
575 }
576
rig_cmp(const struct rig_caps * rig1,const struct rig_caps * rig2)577 static bool rig_cmp(const struct rig_caps* rig1, const struct rig_caps* rig2)
578 {
579 int ret;
580
581 ret = strcasecmp(rig1->mfg_name, rig2->mfg_name);
582 if (ret > 0) return false;
583 if (ret < 0) return true;
584 ret = strcasecmp(rig1->model_name, rig2->model_name);
585 if (ret > 0) return false;
586 if (ret < 0) return true;
587 if (rig1->rig_model < rig2->rig_model)
588 return true;
589 return false;
590 }
591
hamlib_get_rigs(void)592 void hamlib_get_rigs(void)
593 {
594 if (!hamlib_rigs.empty())
595 return;
596
597 enum rig_debug_level_e dblv = RIG_DEBUG_NONE;
598 #ifndef NDEBUG
599 const char* hd = getenv("FLDIGI_HAMLIB_DEBUG");
600 if (hd) {
601 dblv = static_cast<enum rig_debug_level_e>(strtol(hd, NULL, 10));
602 dblv = CLAMP(dblv, RIG_DEBUG_NONE, RIG_DEBUG_TRACE);
603 }
604 #endif
605 rig_set_debug(dblv);
606
607 rig_load_all_backends();
608 rig_list_foreach(add_to_list, 0);
609 sort(hamlib_rigs.begin(), hamlib_rigs.end(), rig_cmp);
610 }
611
hamlib_get_rig_model_compat(const char * name)612 rig_model_t hamlib_get_rig_model_compat(const char* name)
613 {
614 for (rig_list_t::const_iterator i = hamlib_rigs.begin(); i != hamlib_rigs.end(); ++i)
615 if (strstr(name, (*i)->mfg_name) && strstr(name, (*i)->model_name))
616 return (*i)->rig_model;
617 return 0;
618 }
619
hamlib_get_index(rig_model_t model)620 size_t hamlib_get_index(rig_model_t model)
621 {
622 for (rig_list_t::const_iterator i = hamlib_rigs.begin(); i != hamlib_rigs.end(); ++i)
623 if ((*i)->rig_model == model)
624 return i - hamlib_rigs.begin();
625 return hamlib_rigs.size();
626 }
627
hamlib_get_rig_model(size_t i)628 rig_model_t hamlib_get_rig_model(size_t i)
629 {
630 try {
631 return hamlib_rigs.at(i)->rig_model;
632 }
633 catch (...) {
634 return 0;
635 }
636 }
637
hamlib_get_rig_str(int (* func)(const char *))638 void hamlib_get_rig_str(int (*func)(const char*))
639 {
640 string rigstr;
641 for (rig_list_t::const_iterator i = hamlib_rigs.begin(); i != hamlib_rigs.end(); ++i) {
642 rigstr.clear();
643 rigstr.append((*i)->mfg_name).append(" ").append((*i)->model_name);
644 rigstr.append(" (").append(rig_strstatus((*i)->status)).append(")");
645 if (!(*func)(rigstr.c_str()))
646 break;
647 }
648 }
649