gtk-rig-ctrl.c
1 /* 2 Gpredict: Real-time satellite tracking and orbit prediction program 3 4 Copyright (C) 2001-2019 Alexandru Csete, OZ9AEC 5 Copyright (C) 2017 Patrick Dohmen, DL4PD 6 Copyright (C) 2018 Mario Haustein, DM5AHA 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, visit http://www.fsf.org/ 20 */ 21 /* 22 * RIG control window. 23 * 24 * The master radio control UI is implemented as a Gtk+ Widget in order 25 * to allow multiple instances. The widget is created from the module 26 * popup menu and each module can have several radio control windows 27 * attached to it. Note, however, that current implementation only 28 * allows one control window per module. 29 * 30 * TODO Duplex TRX 31 * TODO Transponder passband display somewhere, below Sat freq? 32 * 33 */ 34 #ifdef HAVE_CONFIG_H 35 #include <build-config.h> 36 #endif 37 38 #include <gdk/gdkkeysyms.h> 39 #include <glib.h> 40 #include <glib/gi18n.h> 41 #include <gtk/gtk.h> 42 #include <math.h> 43 44 /* NETWORK */ 45 #ifndef WIN32 46 #include <arpa/inet.h> /* htons() */ 47 #include <netdb.h> /* gethostbyname() */ 48 #include <netinet/in.h> /* struct sockaddr_in */ 49 #include <sys/socket.h> /* socket(), connect(), send() */ 50 #else 51 #include <winsock2.h> 52 #endif 53 54 #include "compat.h" 55 #include "gpredict-utils.h" 56 #include "gtk-freq-knob.h" 57 #include "gtk-rig-ctrl.h" 58 #include "predict-tools.h" 59 #include "radio-conf.h" 60 #include "sat-log.h" 61 #include "sat-cfg.h" 62 #include "trsp-conf.h" 63 64 65 #define AZEL_FMTSTR "%7.2f\302\260" 66 #define MAX_ERROR_COUNT 5 67 #define WR_DEL 5000 /* delay in usec to wait between write and read commands */ 68 69 /* radio control functions */ 70 static void exec_rx_cycle(GtkRigCtrl * ctrl); 71 static void exec_tx_cycle(GtkRigCtrl * ctrl); 72 static void exec_trx_cycle(GtkRigCtrl * ctrl); 73 static void exec_toggle_cycle(GtkRigCtrl * ctrl); 74 static void exec_toggle_tx_cycle(GtkRigCtrl * ctrl); 75 static void exec_duplex_cycle(GtkRigCtrl * ctrl); 76 static void exec_duplex_tx_cycle(GtkRigCtrl * ctrl); 77 static void exec_dual_rig_cycle(GtkRigCtrl * ctrl); 78 static gboolean check_aos_los(GtkRigCtrl * ctrl); 79 static gboolean set_freq_simplex(GtkRigCtrl * ctrl, gint sock, gdouble freq); 80 static gboolean get_freq_simplex(GtkRigCtrl * ctrl, gint sock, gdouble * freq); 81 static gboolean set_freq_toggle(GtkRigCtrl * ctrl, gint sock, gdouble freq); 82 static gboolean set_toggle(GtkRigCtrl * ctrl, gint sock); 83 static gboolean unset_toggle(GtkRigCtrl * ctrl, gint sock); 84 static gboolean get_freq_toggle(GtkRigCtrl * ctrl, gint sock, gdouble * freq); 85 static gboolean get_ptt(GtkRigCtrl * ctrl, gint sock); 86 static gboolean set_ptt(GtkRigCtrl * ctrl, gint sock, gboolean ptt); 87 88 /* add thread for hamlib communication */ 89 gpointer rigctl_run(gpointer data); 90 static void rigctrl_open(GtkRigCtrl * data); 91 static void rigctrl_close(GtkRigCtrl * data); 92 static void setconfig(gpointer data); 93 static void remove_timer(GtkRigCtrl * data); 94 static void start_timer(GtkRigCtrl * data); 95 96 static GtkBoxClass *parent_class = NULL; 97 98 static void gtk_rig_ctrl_destroy(GtkWidget * widget) 99 { 100 GtkRigCtrl *ctrl = GTK_RIG_CTRL(widget); 101 102 if (ctrl->rigctl_thread != NULL) 103 { 104 g_mutex_lock(&ctrl->widgetsync); 105 106 ctrl->engaged = 0; 107 setconfig(ctrl); 108 109 /* synchronization */ 110 g_cond_wait(&ctrl->widgetready, &ctrl->widgetsync); 111 g_mutex_unlock(&ctrl->widgetsync); 112 ctrl->rigctl_thread = NULL; 113 } 114 115 if (ctrl->conf != NULL) 116 { 117 radio_conf_save(ctrl->conf); 118 g_free(ctrl->conf->name); 119 g_free(ctrl->conf->host); 120 g_free(ctrl->conf); 121 ctrl->conf = NULL; 122 } 123 if (ctrl->conf2 != NULL) 124 { 125 g_free(ctrl->conf2->name); 126 g_free(ctrl->conf2->host); 127 g_free(ctrl->conf2); 128 ctrl->conf2 = NULL; 129 } 130 131 if (ctrl->trsplist != NULL) 132 { 133 free_transponders(ctrl->trsplist); 134 ctrl->trsplist = NULL; 135 } 136 137 (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); 138 } 139 140 static void gtk_rig_ctrl_class_init(GtkRigCtrlClass * class, 141 gpointer class_data) 142 { 143 GtkWidgetClass *widget_class; 144 145 (void)class_data; 146 147 widget_class = (GtkWidgetClass *) class; 148 parent_class = g_type_class_peek_parent(class); 149 widget_class->destroy = gtk_rig_ctrl_destroy; 150 } 151 152 static void gtk_rig_ctrl_init(GtkRigCtrl * ctrl, 153 gpointer g_class) 154 { 155 (void)g_class; 156 157 ctrl->sats = NULL; 158 ctrl->target = NULL; 159 ctrl->pass = NULL; 160 ctrl->qth = NULL; 161 ctrl->conf = NULL; 162 ctrl->conf2 = NULL; 163 ctrl->trsp = NULL; 164 ctrl->trsplist = NULL; 165 ctrl->trsplock = FALSE; 166 ctrl->tracking = FALSE; 167 ctrl->prev_ele = 0.0; 168 ctrl->sock = 0; 169 ctrl->sock2 = 0; 170 g_mutex_init(&(ctrl->busy)); 171 ctrl->engaged = FALSE; 172 ctrl->delay = 1000; 173 ctrl->timerid = 0; 174 ctrl->errcnt = 0; 175 ctrl->lastrxptt = FALSE; 176 ctrl->lasttxptt = TRUE; 177 ctrl->lastrxf = 0.0; 178 ctrl->lasttxf = 0.0; 179 ctrl->last_toggle_tx = -1; 180 } 181 182 GType gtk_rig_ctrl_get_type() 183 { 184 static GType gtk_rig_ctrl_type = 0; 185 186 if (!gtk_rig_ctrl_type) 187 { 188 189 static const GTypeInfo gtk_rig_ctrl_info = { 190 sizeof(GtkRigCtrlClass), 191 NULL, /* base_init */ 192 NULL, /* base_finalize */ 193 (GClassInitFunc) gtk_rig_ctrl_class_init, 194 NULL, /* class_finalize */ 195 NULL, /* class_data */ 196 sizeof(GtkRigCtrl), 197 2, /* n_preallocs */ 198 (GInstanceInitFunc) gtk_rig_ctrl_init, 199 NULL 200 }; 201 202 gtk_rig_ctrl_type = g_type_register_static(GTK_TYPE_BOX, 203 "GtkRigCtrl", 204 >k_rig_ctrl_info, 0); 205 } 206 207 return gtk_rig_ctrl_type; 208 } 209 210 211 static void update_count_down(GtkRigCtrl * ctrl, gdouble t) 212 { 213 gdouble targettime; 214 gdouble delta; 215 gchar *buff; 216 guint h, m, s; 217 gchar *aoslos; 218 219 /* select AOS or LOS time depending on target elevation */ 220 if (ctrl->target->el < 0.0) 221 { 222 targettime = ctrl->target->aos; 223 aoslos = g_strdup_printf(_("AOS in")); 224 } 225 else 226 { 227 targettime = ctrl->target->los; 228 aoslos = g_strdup_printf(_("LOS in")); 229 } 230 231 delta = targettime - t; 232 233 /* convert julian date to seconds */ 234 s = (guint) (delta * 86400); 235 236 /* extract hours */ 237 h = (guint) floor(s / 3600); 238 s -= 3600 * h; 239 240 /* extract minutes */ 241 m = (guint) floor(s / 60); 242 s -= 60 * m; 243 244 if (h > 0) 245 buff = 246 g_strdup_printf 247 ("<span size='xx-large'><b>%s %02d:%02d:%02d</b></span>", aoslos, 248 h, m, s); 249 else 250 buff = 251 g_strdup_printf("<span size='xx-large'><b>%s %02d:%02d</b></span>", 252 aoslos, m, s); 253 254 gtk_label_set_markup(GTK_LABEL(ctrl->SatCnt), buff); 255 256 g_free(buff); 257 g_free(aoslos); 258 } 259 260 /* 261 * Update rig control state. 262 * 263 * This function is called by the parent, i.e. GtkSatModule, indicating that 264 * the satellite data has been updated. The function updates the internal state 265 * of the controller and the rigator. 266 */ 267 void gtk_rig_ctrl_update(GtkRigCtrl * ctrl, gdouble t) 268 { 269 gdouble satfreq; 270 gchar *buff; 271 272 g_mutex_lock(&ctrl->rig_ctrl_updatelock); 273 274 if (ctrl->target) 275 { 276 buff = g_strdup_printf(AZEL_FMTSTR, ctrl->target->az); 277 gtk_label_set_text(GTK_LABEL(ctrl->SatAz), buff); 278 g_free(buff); 279 buff = g_strdup_printf(AZEL_FMTSTR, ctrl->target->el); 280 gtk_label_set_text(GTK_LABEL(ctrl->SatEl), buff); 281 g_free(buff); 282 283 update_count_down(ctrl, t); 284 285 if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_IMPERIAL)) 286 { 287 buff = g_strdup_printf("%.0f mi", KM_TO_MI(ctrl->target->range)); 288 } 289 else 290 { 291 buff = g_strdup_printf("%.0f km", ctrl->target->range); 292 } 293 gtk_label_set_text(GTK_LABEL(ctrl->SatRng), buff); 294 g_free(buff); 295 296 if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_IMPERIAL)) 297 { 298 buff = g_strdup_printf("%.3f mi/s", 299 KM_TO_MI(ctrl->target->range_rate)); 300 } 301 else 302 { 303 buff = g_strdup_printf("%.3f km/s", ctrl->target->range_rate); 304 } 305 gtk_label_set_text(GTK_LABEL(ctrl->SatRngRate), buff); 306 g_free(buff); 307 308 /* Doppler shift down */ 309 satfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 310 ctrl->dd = -satfreq * (ctrl->target->range_rate / 299792.4580); // Hz 311 buff = g_strdup_printf("%.0f Hz", ctrl->dd); 312 gtk_label_set_text(GTK_LABEL(ctrl->SatDopDown), buff); 313 g_free(buff); 314 315 /* Doppler shift up */ 316 satfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 317 ctrl->du = satfreq * (ctrl->target->range_rate / 299792.4580); // Hz 318 buff = g_strdup_printf("%.0f Hz", ctrl->du); 319 gtk_label_set_text(GTK_LABEL(ctrl->SatDopUp), buff); 320 g_free(buff); 321 322 /* update next pass if necessary */ 323 if (ctrl->pass != NULL) 324 { 325 if (ctrl->target->aos > ctrl->pass->aos) 326 { 327 free_pass(ctrl->pass); 328 ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); 329 } 330 } 331 else 332 { 333 /* we don't have any current pass; store the current one */ 334 ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); 335 } 336 } 337 338 g_mutex_unlock(&ctrl->rig_ctrl_updatelock); 339 } 340 341 342 /* 343 * Track the downlink frequency by setting the uplink frequency 344 * according to the lower limit of the downlink passband. 345 */ 346 static void track_downlink(GtkRigCtrl * ctrl) 347 { 348 gdouble delta, down, up; 349 350 if (ctrl->trsp == NULL) 351 return; 352 353 /* ensure that we have a usable transponder config */ 354 if ((ctrl->trsp->downlow > 0) && (ctrl->trsp->uplow > 0)) 355 { 356 down = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 357 delta = down - ctrl->trsp->downlow; 358 359 if (ctrl->trsp->invert) 360 up = ctrl->trsp->uphigh - delta; 361 else 362 up = ctrl->trsp->uplow + delta; 363 364 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqUp), up); 365 } 366 } 367 368 /* 369 * Track the uplink frequency by setting the downlink frequency 370 * according to the offset from the lower limit on the uplink passband. 371 */ 372 static void track_uplink(GtkRigCtrl * ctrl) 373 { 374 gdouble delta, down, up; 375 376 if (ctrl->trsp == NULL) 377 return; 378 379 /* ensure that we have a usable transponder config */ 380 if ((ctrl->trsp->downlow > 0) && (ctrl->trsp->uplow > 0)) 381 { 382 up = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 383 delta = up - ctrl->trsp->uplow; 384 385 if (ctrl->trsp->invert) 386 down = ctrl->trsp->downhigh - delta; 387 else 388 down = ctrl->trsp->downlow + delta; 389 390 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqDown), down); 391 } 392 } 393 394 void gtk_rig_ctrl_select_sat(GtkRigCtrl * ctrl, gint catnum) 395 { 396 sat_t *sat; 397 int i, n; 398 399 /* find index in satellite list */ 400 n = g_slist_length(ctrl->sats); 401 for (i = 0; i < n; i++) 402 { 403 sat = SAT(g_slist_nth_data(ctrl->sats, i)); 404 if (sat) 405 { 406 if (sat->tle.catnr == catnum) 407 { 408 /* assume the index is the same in sat selector */ 409 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->SatSel), i); 410 break; 411 } 412 } 413 } 414 } 415 416 static void downlink_changed_cb(GtkFreqKnob * knob, gpointer data) 417 { 418 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 419 420 (void)knob; 421 422 if (ctrl->trsplock) 423 track_downlink(ctrl); 424 } 425 426 static void uplink_changed_cb(GtkFreqKnob * knob, gpointer data) 427 { 428 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 429 430 (void)knob; 431 432 if (ctrl->trsplock) 433 track_uplink(ctrl); 434 } 435 436 /* 437 * Create freq control widgets for downlink. 438 * 439 * This function creates and initialises the widgets for controlling the 440 * downlink frequency. It consists of a controller widget showing the 441 * satellite frequency with the radio frequency below it. 442 * 443 */ 444 static GtkWidget *create_downlink_widgets(GtkRigCtrl * ctrl) 445 { 446 GtkWidget *frame; 447 GtkWidget *vbox; 448 GtkWidget *hbox1, *hbox2; 449 GtkWidget *label; 450 451 label = gtk_label_new(NULL); 452 gtk_label_set_markup(GTK_LABEL(label), _("<b> Downlink </b>")); 453 frame = gtk_frame_new(NULL); 454 gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.5); 455 gtk_frame_set_label_widget(GTK_FRAME(frame), label); 456 457 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 458 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); 459 hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 460 hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 461 462 /* satellite downlink frequency */ 463 ctrl->SatFreqDown = gtk_freq_knob_new(145890000.0, TRUE); 464 g_signal_connect(ctrl->SatFreqDown, "freq-changed", 465 G_CALLBACK(downlink_changed_cb), ctrl); 466 gtk_box_pack_start(GTK_BOX(vbox), ctrl->SatFreqDown, TRUE, TRUE, 0); 467 468 /* Downlink doppler */ 469 label = gtk_label_new(_("Doppler:")); 470 gtk_widget_set_tooltip_text(label, 471 _("The Doppler shift according to the range " 472 "rate and the currently selected downlink " 473 "frequency")); 474 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 475 gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0); 476 ctrl->SatDopDown = gtk_label_new("---- Hz"); 477 g_object_set(ctrl->SatDopDown, "xalign", 0.0f, "yalign", 0.5f, NULL); 478 gtk_box_pack_start(GTK_BOX(hbox1), ctrl->SatDopDown, FALSE, TRUE, 0); 479 480 /* Downconverter LO */ 481 ctrl->LoDown = gtk_label_new("0 MHz"); 482 g_object_set(ctrl->LoDown, "xalign", 1.0f, "yalign", 0.5f, NULL); 483 gtk_box_pack_end(GTK_BOX(hbox1), ctrl->LoDown, FALSE, FALSE, 2); 484 label = gtk_label_new(_("LO:")); 485 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 486 gtk_box_pack_end(GTK_BOX(hbox1), label, FALSE, FALSE, 0); 487 488 /* Radio downlink frequency */ 489 label = gtk_label_new(NULL); 490 gtk_label_set_markup(GTK_LABEL(label), 491 "<span size='large'><b>Radio:</b></span>"); 492 g_object_set(label, "xalign", 0.5f, "yalign", 1.0f, NULL); 493 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, TRUE, 0); 494 ctrl->RigFreqDown = gtk_freq_knob_new(145890000.0, FALSE); 495 gtk_box_pack_start(GTK_BOX(hbox2), ctrl->RigFreqDown, TRUE, TRUE, 0); 496 497 /* finish packing ... */ 498 gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, TRUE, 10); 499 gtk_box_pack_start(GTK_BOX(vbox), hbox2, TRUE, TRUE, 0); 500 gtk_container_add(GTK_CONTAINER(frame), vbox); 501 502 return frame; 503 } 504 505 /* 506 * Create uplink frequency display widgets. 507 * 508 * This function creates and initialises the widgets for displaying the 509 * uplink frequency of the satellite and the radio. 510 */ 511 static GtkWidget *create_uplink_widgets(GtkRigCtrl * ctrl) 512 { 513 GtkWidget *frame; 514 GtkWidget *vbox; 515 GtkWidget *hbox1, *hbox2; 516 GtkWidget *label; 517 518 label = gtk_label_new(NULL); 519 gtk_label_set_markup(GTK_LABEL(label), _("<b> Uplink </b>")); 520 frame = gtk_frame_new(NULL); 521 gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.5); 522 gtk_frame_set_label_widget(GTK_FRAME(frame), label); 523 524 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 525 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); 526 hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 527 hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 528 529 /* satellite uplink frequency */ 530 ctrl->SatFreqUp = gtk_freq_knob_new(145890000.0, TRUE); 531 g_signal_connect(ctrl->SatFreqUp, "freq-changed", 532 G_CALLBACK(uplink_changed_cb), ctrl); 533 gtk_box_pack_start(GTK_BOX(vbox), ctrl->SatFreqUp, TRUE, TRUE, 0); 534 535 /* Uplink doppler */ 536 label = gtk_label_new(_("Doppler:")); 537 gtk_widget_set_tooltip_text(label, 538 _("The Doppler shift according to the range " 539 "rate and the currently selected downlink " 540 "frequency")); 541 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 542 gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0); 543 ctrl->SatDopUp = gtk_label_new("---- Hz"); 544 g_object_set(ctrl->SatDopUp, "xalign", 0.0f, "yalign", 0.5f, NULL); 545 gtk_box_pack_start(GTK_BOX(hbox1), ctrl->SatDopUp, FALSE, TRUE, 0); 546 547 /* Upconverter LO */ 548 ctrl->LoUp = gtk_label_new("0 MHz"); 549 g_object_set(ctrl->LoUp, "xalign", 1.0f, "yalign", 0.5f, NULL); 550 gtk_box_pack_end(GTK_BOX(hbox1), ctrl->LoUp, FALSE, FALSE, 2); 551 label = gtk_label_new(_("LO:")); 552 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 553 gtk_box_pack_end(GTK_BOX(hbox1), label, FALSE, FALSE, 0); 554 555 /* Radio uplink frequency */ 556 label = gtk_label_new(NULL); 557 gtk_label_set_markup(GTK_LABEL(label), 558 "<span size='large'><b>Radio:</b></span>"); 559 g_object_set(label, "xalign", 0.5f, "yalign", 1.0f, NULL); 560 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, TRUE, 0); 561 ctrl->RigFreqUp = gtk_freq_knob_new(145890000.0, FALSE); 562 gtk_box_pack_start(GTK_BOX(hbox2), ctrl->RigFreqUp, TRUE, TRUE, 0); 563 564 gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, TRUE, 10); 565 gtk_box_pack_start(GTK_BOX(vbox), hbox2, TRUE, TRUE, 0); 566 gtk_container_add(GTK_CONTAINER(frame), vbox); 567 568 return frame; 569 } 570 571 572 static void load_trsp_list(GtkRigCtrl * ctrl) 573 { 574 trsp_t *trsp = NULL; 575 guint i, n; 576 577 if (ctrl->trsplist != NULL) 578 { 579 n = g_slist_length(ctrl->trsplist); 580 for (i = 0; i < n; i++) 581 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(ctrl->TrspSel), 0); 582 583 free_transponders(ctrl->trsplist); 584 ctrl->trsp = NULL; 585 } 586 587 /* check if there is a target satellite */ 588 if (ctrl->target == NULL) 589 { 590 sat_log_log(SAT_LOG_LEVEL_INFO, 591 _("%s:%s: GtkSatModule has no target satellite."), 592 __FILE__, __func__); 593 return; 594 } 595 596 /* read transponders for new target */ 597 ctrl->trsplist = read_transponders(ctrl->target->tle.catnr); 598 n = g_slist_length(ctrl->trsplist); 599 sat_log_log(SAT_LOG_LEVEL_DEBUG, 600 _("%s:%s: Satellite %d has %d transponder modes."), 601 __FILE__, __func__, ctrl->target->tle.catnr, n); 602 603 if (n == 0) 604 return; 605 606 for (i = 0; i < n; i++) 607 { 608 trsp = (trsp_t *) g_slist_nth_data(ctrl->trsplist, i); 609 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ctrl->TrspSel), 610 trsp->name); 611 612 sat_log_log(SAT_LOG_LEVEL_DEBUG, 613 _("%s:%s: Read transponder '%s' for satellite %d"), 614 __FILE__, __func__, trsp->name, ctrl->target->tle.catnr); 615 } 616 617 ctrl->trsp = (trsp_t *) g_slist_nth_data(ctrl->trsplist, 0); 618 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->TrspSel), 0); 619 } 620 621 static gboolean have_conf() 622 { 623 GDir *dir = NULL; /* directory handle */ 624 GError *error = NULL; /* error flag and info */ 625 gchar *dirname; /* directory name */ 626 const gchar *filename; /* file name */ 627 gint i = 0; 628 629 dirname = get_hwconf_dir(); 630 dir = g_dir_open(dirname, 0, &error); 631 if (dir) 632 { 633 while ((filename = g_dir_read_name(dir))) 634 { 635 if (g_str_has_suffix(filename, ".rig")) 636 { 637 i++; 638 break; 639 } 640 } 641 } 642 else 643 { 644 sat_log_log(SAT_LOG_LEVEL_ERROR, 645 _("%s:%d: Failed to open hwconf dir (%s)"), 646 __FILE__, __LINE__, error->message); 647 g_clear_error(&error); 648 } 649 650 g_free(dirname); 651 g_dir_close(dir); 652 653 return (i > 0) ? TRUE : FALSE; 654 } 655 656 /* Called when the user selects a new satellite. */ 657 static void sat_selected_cb(GtkComboBox * satsel, gpointer data) 658 { 659 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 660 gint i; 661 662 i = gtk_combo_box_get_active(satsel); 663 if (i >= 0) 664 { 665 ctrl->target = SAT(g_slist_nth_data(ctrl->sats, i)); 666 667 ctrl->prev_ele = ctrl->target->el; 668 669 /* update next pass */ 670 if (ctrl->pass != NULL) 671 free_pass(ctrl->pass); 672 ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); 673 674 /* read transponders for new target */ 675 load_trsp_list(ctrl); 676 } 677 else 678 { 679 sat_log_log(SAT_LOG_LEVEL_ERROR, 680 _("%s:%s: Invalid satellite selection: %d"), 681 __FILE__, __func__, i); 682 683 /* clear pass just in case... */ 684 if (ctrl->pass != NULL) 685 { 686 free_pass(ctrl->pass); 687 ctrl->pass = NULL; 688 } 689 } 690 } 691 692 /* 693 * Manage "Tune" events 694 * 695 * @param button Pointer to the GtkButton that received the signal. 696 * @param data Pointer to the GtkRigCtrl structure. 697 * 698 * This function is called when the user clicks on the Tune button next to the 699 * transponder selector. When clicked, the radio controller will set the RX and TX 700 * frequencies to the middle of the transponder uplink/downlink bands. 701 * 702 * To avoid conflicts with manual frequency changes on the radio, the sync between 703 * RIG and GPREDICT is invalidated after the tuning operation is performed. 704 */ 705 static void trsp_tune_cb(GtkButton * button, gpointer data) 706 { 707 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 708 gdouble freq; 709 710 (void)button; 711 712 if (ctrl->trsp == NULL) 713 return; 714 715 /* tune downlink */ 716 if ((ctrl->trsp->downlow > 0) && (ctrl->trsp->downhigh > 0)) 717 { 718 freq = ctrl->trsp->downlow + 719 labs((long)ctrl->trsp->downhigh - (long)ctrl->trsp->downlow) / 2; 720 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqDown), freq); 721 722 /* invalidate RIG<->GPREDICT sync */ 723 ctrl->lastrxf = 0.0; 724 } 725 726 /* tune uplink */ 727 if ((ctrl->trsp->uplow > 0) && (ctrl->trsp->uphigh > 0)) 728 { 729 freq = ctrl->trsp->uplow + 730 labs((long)ctrl->trsp->uphigh - (long)ctrl->trsp->uplow) / 2; 731 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqUp), freq); 732 733 /* invalidate RIG<->GPREDICT sync */ 734 ctrl->lasttxf = 0.0; 735 } 736 } 737 738 /* 739 * Called when a new transponder is selected. 740 * It updates ctrl->trsp with the new selection and issues a "tune" event. 741 */ 742 static void trsp_selected_cb(GtkComboBox * box, gpointer data) 743 { 744 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 745 gint i, n; 746 747 i = gtk_combo_box_get_active(box); 748 n = g_slist_length(ctrl->trsplist); 749 750 if (i == -1) 751 { 752 /* clear transponder data */ 753 ctrl->trsp = NULL; 754 } 755 else if (i < n) 756 { 757 ctrl->trsp = (trsp_t *) g_slist_nth_data(ctrl->trsplist, i); 758 trsp_tune_cb(NULL, data); 759 } 760 else 761 { 762 sat_log_log(SAT_LOG_LEVEL_ERROR, 763 _("%s: Inconsistency detected in internal transponder " 764 "data (%d,%d)"), __func__, i, n); 765 } 766 } 767 768 /* 769 * Manage lock transponder signals. 770 * 771 * @param button Pointer to the GtkToggleButton that received the signal. 772 * @param data Pointer to the GtkRigCtrl structure. 773 * 774 * This function is called when the user toggles the "Lock Transponder" button. 775 * When ON, the uplink and downlink are locked according to the current transponder 776 * data, i.e. when user changes the downlink, the uplink will follow automatically 777 * taking into account whether the transponder is inverting or not. 778 */ 779 static void trsp_lock_cb(GtkToggleButton * button, gpointer data) 780 { 781 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 782 783 ctrl->trsplock = gtk_toggle_button_get_active(button); 784 785 /* set uplink according to downlink */ 786 if (ctrl->trsplock) 787 track_downlink(ctrl); 788 } 789 790 static void track_toggle_cb(GtkToggleButton * button, gpointer data) 791 { 792 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 793 794 ctrl->tracking = gtk_toggle_button_get_active(button); 795 796 /* invalidate sync with radio */ 797 ctrl->lastrxf = 0.0; 798 ctrl->lasttxf = 0.0; 799 } 800 801 /* Called when the user changes the value of the cycle delay */ 802 static void delay_changed_cb(GtkSpinButton * spin, gpointer data) 803 { 804 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 805 806 ctrl->delay = (guint) gtk_spin_button_get_value(spin); 807 if (ctrl->conf) 808 ctrl->conf->cycle = ctrl->delay; 809 810 if (ctrl->engaged) 811 start_timer(ctrl); 812 } 813 814 static void primary_rig_selected_cb(GtkComboBox * box, gpointer data) 815 { 816 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 817 gchar *buff; 818 819 sat_log_log(SAT_LOG_LEVEL_DEBUG, 820 _("%s:%s: Primary device selected: %d"), 821 __FILE__, __func__, gtk_combo_box_get_active(box)); 822 823 if (ctrl->conf != NULL) 824 { 825 g_free(ctrl->conf->name); 826 g_free(ctrl->conf->host); 827 g_free(ctrl->conf); 828 } 829 830 ctrl->conf = g_try_new(radio_conf_t, 1); 831 if (ctrl->conf == NULL) 832 { 833 sat_log_log(SAT_LOG_LEVEL_ERROR, 834 _("%s:%d: Failed to allocate memory for radio config"), 835 __FILE__, __LINE__); 836 return; 837 } 838 839 ctrl->conf->name = 840 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(box)); 841 if (radio_conf_read(ctrl->conf)) 842 { 843 sat_log_log(SAT_LOG_LEVEL_INFO, 844 _("%s:%s: Loaded new radio configuration %s"), 845 __FILE__, __func__, ctrl->conf->name); 846 847 gtk_spin_button_set_value(GTK_SPIN_BUTTON(ctrl->cycle_spin), 848 ctrl->conf->cycle); 849 850 /* update LO widgets */ 851 buff = g_strdup_printf(_("%.0f MHz"), ctrl->conf->lo / 1.0e6); 852 gtk_label_set_text(GTK_LABEL(ctrl->LoDown), buff); 853 g_free(buff); 854 /* uplink LO only if single device */ 855 if (ctrl->conf2 == NULL) 856 { 857 buff = g_strdup_printf(_("%.0f MHz"), ctrl->conf->loup / 1.0e6); 858 gtk_label_set_text(GTK_LABEL(ctrl->LoUp), buff); 859 g_free(buff); 860 } 861 } 862 else 863 { 864 sat_log_log(SAT_LOG_LEVEL_ERROR, 865 _("%s:%s: Failed to load radio configuration %s"), 866 __FILE__, __func__, ctrl->conf->name); 867 868 g_free(ctrl->conf->name); 869 if (ctrl->conf->host) 870 g_free(ctrl->conf->host); 871 g_free(ctrl->conf); 872 ctrl->conf = NULL; 873 } 874 } 875 876 static void secondary_rig_selected_cb(GtkComboBox * box, gpointer data) 877 { 878 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 879 gchar *buff; 880 gchar *name1, *name2; 881 882 883 sat_log_log(SAT_LOG_LEVEL_DEBUG, 884 _("%s:%s: Secondary device selected: %d"), 885 __FILE__, __func__, gtk_combo_box_get_active(box)); 886 887 if (ctrl->conf2 != NULL) 888 { 889 g_free(ctrl->conf2->name); 890 g_free(ctrl->conf2->host); 891 g_free(ctrl->conf2); 892 ctrl->conf2 = NULL; 893 } 894 895 if (gtk_combo_box_get_active(box) == 0) 896 { 897 /* first entry is "None" */ 898 899 /* reset uplink LO to what's in ctrl->conf */ 900 if (ctrl->conf != NULL) 901 { 902 buff = g_strdup_printf(_("%.0f MHz"), ctrl->conf->loup / 1.0e6); 903 gtk_label_set_text(GTK_LABEL(ctrl->LoUp), buff); 904 g_free(buff); 905 } 906 907 return; 908 } 909 910 /* ensure that selected secondary rig is not the same as the primary */ 911 name1 = 912 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ctrl->DevSel)); 913 name2 = 914 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ctrl->DevSel2)); 915 if (!g_strcmp0(name1, name2)) 916 { 917 /* selected conf is the same as the primary one */ 918 g_free(name1); 919 g_free(name2); 920 if (ctrl->conf != NULL) 921 { 922 buff = g_strdup_printf(_("%.0f MHz"), ctrl->conf->loup / 1.0e6); 923 gtk_label_set_text(GTK_LABEL(ctrl->LoUp), buff); 924 g_free(buff); 925 } 926 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->DevSel2), 0); 927 928 return; 929 } 930 931 g_free(name1); 932 g_free(name2); 933 934 /* else load new device */ 935 ctrl->conf2 = g_try_new(radio_conf_t, 1); 936 if (ctrl->conf2 == NULL) 937 { 938 sat_log_log(SAT_LOG_LEVEL_ERROR, 939 _("%s:%s: Failed to allocate memory for radio config"), 940 __FILE__, __func__); 941 return; 942 } 943 944 /* load new configuration */ 945 ctrl->conf2->name = 946 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(box)); 947 if (radio_conf_read(ctrl->conf2)) 948 { 949 sat_log_log(SAT_LOG_LEVEL_INFO, 950 _("%s:%s: Loaded new radio configuration %s"), 951 __FILE__, __func__, ctrl->conf2->name); 952 953 buff = g_strdup_printf(_("%.0f MHz"), ctrl->conf2->loup / 1.0e6); 954 gtk_label_set_text(GTK_LABEL(ctrl->LoUp), buff); 955 g_free(buff); 956 } 957 else 958 { 959 sat_log_log(SAT_LOG_LEVEL_ERROR, 960 _("%s:%s: Failed to load radio configuration %s"), 961 __FILE__, __func__, ctrl->conf->name); 962 963 g_free(ctrl->conf2->name); 964 if (ctrl->conf2->host) 965 g_free(ctrl->conf2->host); 966 g_free(ctrl->conf2); 967 ctrl->conf2 = NULL; 968 } 969 } 970 971 static void rig_engaged_cb(GtkToggleButton * button, gpointer data) 972 { 973 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 974 975 if (ctrl->conf == NULL) 976 { 977 /* we don't have a working configuration */ 978 sat_log_log(SAT_LOG_LEVEL_ERROR, 979 _("%s: Controller does not have a valid configuration"), 980 __func__); 981 return; 982 } 983 984 if (!gtk_toggle_button_get_active(button)) 985 { 986 /* close socket */ 987 gtk_widget_set_sensitive(ctrl->DevSel, TRUE); 988 gtk_widget_set_sensitive(ctrl->DevSel2, TRUE); 989 ctrl->engaged = FALSE; 990 991 /* stop worker thread... */ 992 setconfig(ctrl); 993 ctrl->rigctl_thread = NULL; 994 } 995 else 996 { 997 gtk_widget_set_sensitive(ctrl->DevSel, FALSE); 998 gtk_widget_set_sensitive(ctrl->DevSel2, FALSE); 999 ctrl->engaged = TRUE; 1000 1001 /* start worker thread... */ 1002 ctrl->rigctlq = g_async_queue_new(); 1003 ctrl->rigctl_thread = g_thread_new("rigctl_run", rigctl_run, ctrl); 1004 setconfig(ctrl); 1005 } 1006 ctrl->conf2 = NULL; 1007 } 1008 1009 static GtkWidget *create_target_widgets(GtkRigCtrl * ctrl) 1010 { 1011 GtkWidget *frame, *table, *label, *track; 1012 GtkWidget *tune, *trsplock, *hbox; 1013 gchar *buff; 1014 guint i, n; 1015 sat_t *sat = NULL; 1016 1017 buff = g_strdup_printf(AZEL_FMTSTR, 0.0); 1018 1019 table = gtk_grid_new(); 1020 gtk_container_set_border_width(GTK_CONTAINER(table), 5); 1021 gtk_grid_set_column_spacing(GTK_GRID(table), 5); 1022 gtk_grid_set_row_spacing(GTK_GRID(table), 5); 1023 1024 /* sat selector */ 1025 ctrl->SatSel = gtk_combo_box_text_new(); 1026 n = g_slist_length(ctrl->sats); 1027 for (i = 0; i < n; i++) 1028 { 1029 sat = SAT(g_slist_nth_data(ctrl->sats, i)); 1030 if (sat) 1031 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ctrl->SatSel), 1032 sat->nickname); 1033 } 1034 1035 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->SatSel), 0); 1036 gtk_widget_set_tooltip_text(ctrl->SatSel, _("Select target object")); 1037 g_signal_connect(ctrl->SatSel, "changed", G_CALLBACK(sat_selected_cb), 1038 ctrl); 1039 gtk_grid_attach(GTK_GRID(table), ctrl->SatSel, 0, 0, 3, 1); 1040 1041 /* tracking button */ 1042 track = gtk_toggle_button_new_with_label(_("Track")); 1043 gtk_widget_set_tooltip_text(track, 1044 _("Track the satellite transponder.\n" 1045 "Enabling this button will apply Doppler " 1046 "correction to the frequency of the radio.")); 1047 gtk_grid_attach(GTK_GRID(table), track, 3, 0, 1, 1); 1048 g_signal_connect(track, "toggled", G_CALLBACK(track_toggle_cb), ctrl); 1049 1050 /* Transponder selector, tune, and trsplock buttons */ 1051 ctrl->TrspSel = gtk_combo_box_text_new(); 1052 gtk_widget_set_tooltip_text(ctrl->TrspSel, _("Select a transponder")); 1053 load_trsp_list(ctrl); 1054 g_signal_connect(ctrl->TrspSel, "changed", G_CALLBACK(trsp_selected_cb), 1055 ctrl); 1056 gtk_grid_attach(GTK_GRID(table), ctrl->TrspSel, 0, 1, 3, 1); 1057 1058 /* buttons */ 1059 tune = gtk_button_new_with_label(_("T")); 1060 gtk_widget_set_tooltip_text(tune, 1061 _("Tune the radio to this transponder. " 1062 "The uplink and downlink will be set to the " 1063 "center of the transponder passband. In case " 1064 "of beacons, only the downlink will be tuned " 1065 "to the beacon frequency.")); 1066 g_signal_connect(tune, "clicked", G_CALLBACK(trsp_tune_cb), ctrl); 1067 1068 trsplock = gtk_toggle_button_new_with_label(_("L")); 1069 gtk_widget_set_tooltip_text(trsplock, 1070 _("Lock the uplink and the downlink to each " 1071 "other. Whenever you change the downlink " 1072 "(in the controller or on the dial, the " 1073 "uplink will track it according to whether " 1074 "the transponder is inverting or not. " 1075 "Similarly, if you change the uplink the " 1076 "downlink will track it automatically.\n\n" 1077 "If the downlink and uplink are initially " 1078 "out of sync when you enable this function, " 1079 "the current downlink frequency will be used " 1080 "as baseline for setting the new uplink " 1081 "frequency.")); 1082 g_signal_connect(trsplock, "toggled", G_CALLBACK(trsp_lock_cb), ctrl); 1083 1084 /* box for packing buttons */ 1085 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); 1086 gtk_box_pack_start(GTK_BOX(hbox), tune, TRUE, TRUE, 0); 1087 gtk_box_pack_start(GTK_BOX(hbox), trsplock, TRUE, TRUE, 0); 1088 gtk_grid_attach(GTK_GRID(table), hbox, 3, 1, 1, 1); 1089 1090 /* Azimuth */ 1091 label = gtk_label_new(_("Az:")); 1092 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1093 gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1); 1094 ctrl->SatAz = gtk_label_new(buff); 1095 g_object_set(ctrl->SatAz, "xalign", 1.0f, "yalign", 0.5f, NULL); 1096 gtk_grid_attach(GTK_GRID(table), ctrl->SatAz, 1, 2, 1, 1); 1097 1098 /* Elevation */ 1099 label = gtk_label_new(_("El:")); 1100 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1101 gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1); 1102 ctrl->SatEl = gtk_label_new(buff); 1103 g_object_set(ctrl->SatEl, "xalign", 1.0f, "yalign", 0.5f, NULL); 1104 gtk_grid_attach(GTK_GRID(table), ctrl->SatEl, 1, 3, 1, 1); 1105 1106 /* Range */ 1107 label = gtk_label_new(_(" Range:")); 1108 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1109 gtk_grid_attach(GTK_GRID(table), label, 2, 2, 1, 1); 1110 ctrl->SatRng = gtk_label_new("0 km"); 1111 g_object_set(ctrl->SatRng, "xalign", 0.0f, "yalign", 0.5f, NULL); 1112 gtk_grid_attach(GTK_GRID(table), ctrl->SatRng, 3, 2, 1, 1); 1113 1114 gtk_widget_set_tooltip_text(label, 1115 _("This is the current distance between the " 1116 "satellite and the observer.")); 1117 gtk_widget_set_tooltip_text(ctrl->SatRng, 1118 _("This is the current distance between the " 1119 "satellite and the observer.")); 1120 1121 /* Range rate */ 1122 label = gtk_label_new(_(" Rate:")); 1123 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1124 gtk_grid_attach(GTK_GRID(table), label, 2, 3, 1, 1); 1125 ctrl->SatRngRate = gtk_label_new("0.0 km/s"); 1126 g_object_set(ctrl->SatRngRate, "xalign", 0.0f, "yalign", 0.5f, NULL); 1127 gtk_grid_attach(GTK_GRID(table), ctrl->SatRngRate, 3, 3, 1, 1); 1128 1129 gtk_widget_set_tooltip_text(label, 1130 _("The rate of change for the distance between" 1131 " the satellite and the observer.")); 1132 gtk_widget_set_tooltip_text(ctrl->SatRngRate, 1133 _("The rate of change for the distance between" 1134 " the satellite and the observer.")); 1135 1136 frame = gtk_frame_new(_("Target")); 1137 gtk_container_add(GTK_CONTAINER(frame), table); 1138 g_free(buff); 1139 1140 return frame; 1141 } 1142 1143 static gboolean is_rig_tx_capable(const gchar * confname) 1144 { 1145 radio_conf_t *conf = NULL; 1146 gboolean cantx = FALSE; 1147 1148 conf = g_try_new(radio_conf_t, 1); 1149 if (conf == NULL) 1150 { 1151 sat_log_log(SAT_LOG_LEVEL_ERROR, 1152 _("%s:%d: Failed to allocate memory for radio config"), 1153 __FILE__, __LINE__); 1154 return FALSE; 1155 } 1156 1157 /* load new configuration */ 1158 conf->name = g_strdup(confname); 1159 if (radio_conf_read(conf)) 1160 { 1161 cantx = (conf->type == RIG_TYPE_RX) ? FALSE : TRUE; 1162 } 1163 else 1164 { 1165 sat_log_log(SAT_LOG_LEVEL_ERROR, 1166 _("%s:%d: Error reading radio configuration %s"), 1167 __FILE__, __LINE__, confname); 1168 1169 cantx = FALSE; 1170 } 1171 1172 g_free(conf->name); 1173 if (conf->host) 1174 g_free(conf->host); 1175 g_free(conf); 1176 1177 return cantx; 1178 } 1179 1180 /* Sort the list of satellites in the combo box. */ 1181 static gint sat_name_compare(sat_t * a, sat_t * b) 1182 { 1183 return (gpredict_strcmp(a->nickname, b->nickname)); 1184 } 1185 1186 /* Sort the list of rigs in the combo box */ 1187 static gint rig_name_compare(const gchar * a, const gchar * b) 1188 { 1189 return (gpredict_strcmp(a, b)); 1190 } 1191 1192 static GtkWidget *create_conf_widgets(GtkRigCtrl * ctrl) 1193 { 1194 GtkWidget *frame, *table, *label; 1195 GDir *dir = NULL; /* directory handle */ 1196 GError *error = NULL; /* error flag and info */ 1197 gchar *dirname; /* directory name */ 1198 gchar **vbuff; 1199 const gchar *filename; /* file name */ 1200 gchar *rigname; 1201 1202 1203 table = gtk_grid_new(); 1204 gtk_container_set_border_width(GTK_CONTAINER(table), 5); 1205 gtk_grid_set_column_spacing(GTK_GRID(table), 5); 1206 gtk_grid_set_row_spacing(GTK_GRID(table), 5); 1207 1208 /* Primary device */ 1209 label = gtk_label_new(_("1. Device:")); 1210 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1211 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); 1212 1213 ctrl->DevSel = gtk_combo_box_text_new(); 1214 gtk_widget_set_tooltip_text(ctrl->DevSel, 1215 _("Select primary radio device." 1216 "This device will be used for downlink and " 1217 "uplink unless you select a secondary device" 1218 " for uplink")); 1219 1220 /* open configuration directory */ 1221 dirname = get_hwconf_dir(); 1222 1223 dir = g_dir_open(dirname, 0, &error); 1224 if (dir) 1225 { 1226 /* read each .rig file */ 1227 GSList *rigs = NULL; 1228 gint i; 1229 gint n; 1230 1231 while ((filename = g_dir_read_name(dir))) 1232 { 1233 if (g_str_has_suffix(filename, ".rig")) 1234 { 1235 vbuff = g_strsplit(filename, ".rig", 0); 1236 rigs = g_slist_insert_sorted(rigs, g_strdup(vbuff[0]), 1237 (GCompareFunc) rig_name_compare); 1238 g_strfreev(vbuff); 1239 } 1240 } 1241 n = g_slist_length(rigs); 1242 for (i = 0; i < n; i++) 1243 { 1244 rigname = g_slist_nth_data(rigs, i); 1245 if (rigname) 1246 { 1247 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT 1248 (ctrl->DevSel), rigname); 1249 g_free(rigname); 1250 } 1251 } 1252 g_slist_free(rigs); 1253 } 1254 else 1255 { 1256 sat_log_log(SAT_LOG_LEVEL_ERROR, 1257 _("%s:%d: Failed to open hwconf dir (%s)"), 1258 __FILE__, __LINE__, error->message); 1259 g_clear_error(&error); 1260 } 1261 1262 g_dir_close(dir); 1263 1264 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->DevSel), 0); 1265 g_signal_connect(ctrl->DevSel, "changed", 1266 G_CALLBACK(primary_rig_selected_cb), ctrl); 1267 gtk_grid_attach(GTK_GRID(table), ctrl->DevSel, 1, 0, 1, 1); 1268 1269 /* Secondary device */ 1270 label = gtk_label_new(_("2. Device:")); 1271 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1272 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1); 1273 1274 ctrl->DevSel2 = gtk_combo_box_text_new(); 1275 gtk_widget_set_tooltip_text(ctrl->DevSel2, 1276 _("Select secondary radio device\n" 1277 "This device will be used for uplink")); 1278 1279 /* load config */ 1280 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ctrl->DevSel2), 1281 _("None")); 1282 gtk_combo_box_set_active(GTK_COMBO_BOX(ctrl->DevSel2), 0); 1283 1284 dir = g_dir_open(dirname, 0, &error); 1285 if (dir) 1286 { 1287 /* read each .rig file */ 1288 while ((filename = g_dir_read_name(dir))) 1289 { 1290 if (g_str_has_suffix(filename, ".rig")) 1291 { 1292 /* only add TX capable rigs */ 1293 vbuff = g_strsplit(filename, ".rig", 0); 1294 if (is_rig_tx_capable(vbuff[0])) 1295 { 1296 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT 1297 (ctrl->DevSel2), vbuff[0]); 1298 } 1299 g_strfreev(vbuff); 1300 } 1301 } 1302 } 1303 else 1304 { 1305 sat_log_log(SAT_LOG_LEVEL_ERROR, 1306 _("%s:%d: Failed to open hwconf dir (%s)"), 1307 __FILE__, __LINE__, error->message); 1308 g_clear_error(&error); 1309 } 1310 1311 g_free(dirname); 1312 g_dir_close(dir); 1313 1314 g_signal_connect(ctrl->DevSel2, "changed", 1315 G_CALLBACK(secondary_rig_selected_cb), ctrl); 1316 gtk_grid_attach(GTK_GRID(table), ctrl->DevSel2, 1, 1, 1, 1); 1317 1318 /* Engage button */ 1319 ctrl->LockBut = gtk_toggle_button_new_with_label(_("Engage")); 1320 gtk_widget_set_tooltip_text(ctrl->LockBut, 1321 _("Engage the selected radio device")); 1322 g_signal_connect(ctrl->LockBut, "toggled", G_CALLBACK(rig_engaged_cb), 1323 ctrl); 1324 gtk_grid_attach(GTK_GRID(table), ctrl->LockBut, 2, 0, 1, 1); 1325 1326 /* cycle period */ 1327 label = gtk_label_new(_("Cycle:")); 1328 g_object_set(label, "xalign", 1.0f, "yalign", 0.5f, NULL); 1329 gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1); 1330 1331 ctrl->cycle_spin = gtk_spin_button_new_with_range(10, 10000, 10); 1332 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(ctrl->cycle_spin), 0); 1333 gtk_widget_set_tooltip_text(ctrl->cycle_spin, 1334 _("This parameter controls the delay between " 1335 "commands sent to the rig.")); 1336 g_signal_connect(ctrl->cycle_spin, "value-changed", 1337 G_CALLBACK(delay_changed_cb), ctrl); 1338 gtk_grid_attach(GTK_GRID(table), ctrl->cycle_spin, 1, 3, 1, 1); 1339 1340 label = gtk_label_new(_("msec")); 1341 g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL); 1342 gtk_grid_attach(GTK_GRID(table), label, 2, 3, 1, 1); 1343 1344 frame = gtk_frame_new(_("Settings")); 1345 gtk_container_add(GTK_CONTAINER(frame), table); 1346 1347 /* load primary config */ 1348 primary_rig_selected_cb(GTK_COMBO_BOX(ctrl->DevSel), ctrl); 1349 1350 return frame; 1351 } 1352 1353 1354 /* Create count down widget */ 1355 static GtkWidget *create_count_down_widgets(GtkRigCtrl * ctrl) 1356 { 1357 GtkWidget *frame; 1358 1359 /* create delta-t label */ 1360 ctrl->SatCnt = gtk_label_new(NULL); 1361 gtk_label_set_markup(GTK_LABEL(ctrl->SatCnt), 1362 _("<span size='large'><b>\316\224T: " 1363 "00:00:00</b></span>")); 1364 gtk_widget_set_tooltip_text(ctrl->SatCnt, 1365 _("The time remaining until the next AOS or " 1366 "LOS event")); 1367 g_object_set(ctrl->SatCnt, "xalign", 0.5f, "yalign", 0.5f, NULL); 1368 gtk_widget_set_margin_top(ctrl->SatCnt, 3); 1369 gtk_widget_set_margin_bottom(ctrl->SatCnt, 3); 1370 1371 frame = gtk_frame_new(NULL); 1372 gtk_container_add(GTK_CONTAINER(frame), ctrl->SatCnt); 1373 1374 return frame; 1375 } 1376 1377 /* Copy satellite from hash table to singly linked list. */ 1378 static void store_sats(gpointer key, gpointer value, gpointer user_data) 1379 { 1380 GtkRigCtrl *ctrl = GTK_RIG_CTRL(user_data); 1381 sat_t *sat = SAT(value); 1382 1383 (void)key; 1384 1385 ctrl->sats = g_slist_insert_sorted(ctrl->sats, sat, 1386 (GCompareFunc) sat_name_compare); 1387 } 1388 1389 static gboolean _send_rigctld_command(GtkRigCtrl * ctrl, gint sock, 1390 gchar * buff, gchar * buffout, 1391 gint sizeout) 1392 { 1393 gint written; 1394 gint size; 1395 1396 size = strlen(buff); 1397 1398 sat_log_log(SAT_LOG_LEVEL_DEBUG, 1399 _("%s:%s: sending %d bytes to rigctld as \"%s\""), 1400 __FILE__, __func__, size, buff); 1401 /* send command */ 1402 written = send(sock, buff, strlen(buff), 0); 1403 if (written != size) 1404 { 1405 sat_log_log(SAT_LOG_LEVEL_ERROR, 1406 _("%s: SIZE ERROR %d / %d"), __func__, written, size); 1407 } 1408 if (written == -1) 1409 { 1410 sat_log_log(SAT_LOG_LEVEL_ERROR, 1411 _("%s: rigctld port closed"), __func__); 1412 return FALSE; 1413 } 1414 /* try to read answer */ 1415 size = recv(sock, buffout, sizeout - 1, 0); 1416 if (size == -1) 1417 { 1418 sat_log_log(SAT_LOG_LEVEL_ERROR, 1419 _("%s: rigctld port closed"), __func__); 1420 return FALSE; 1421 } 1422 1423 buffout[size] = '\0'; 1424 if (size == 0) 1425 { 1426 sat_log_log(SAT_LOG_LEVEL_ERROR, 1427 _("%s:%s: Got 0 bytes from rigctld"), __FILE__, __func__); 1428 } 1429 else 1430 { 1431 sat_log_log(SAT_LOG_LEVEL_DEBUG, 1432 _("%s:%s: Read %d bytes from rigctld"), 1433 __FILE__, __func__, size); 1434 } 1435 ctrl->wrops++; 1436 1437 return TRUE; 1438 } 1439 1440 static gboolean send_rigctld_command(GtkRigCtrl * ctrl, gint sock, 1441 gchar * buff, gchar * buffout, 1442 gint sizeout) 1443 { 1444 gboolean retval; 1445 1446 /* Enter critical section! */ 1447 g_mutex_lock(&ctrl->writelock); 1448 1449 retval = _send_rigctld_command(ctrl, sock, buff, buffout, sizeout); 1450 1451 /* Leave critical section! */ 1452 g_mutex_unlock(&ctrl->writelock); 1453 return (retval); 1454 } 1455 1456 static inline gboolean check_set_response(gchar * buffback, gboolean retcode, 1457 const gchar * function) 1458 { 1459 if (retcode == TRUE) 1460 { 1461 if (strncmp(buffback, "RPRT 0", 6) != 0) 1462 { 1463 sat_log_log(SAT_LOG_LEVEL_ERROR, 1464 _("%s:%s: %s rigctld returned error (%s)"), 1465 __FILE__, __func__, function, buffback); 1466 1467 retcode = FALSE; 1468 } 1469 } 1470 1471 return retcode; 1472 } 1473 1474 static inline gboolean check_get_response(gchar * buffback, gboolean retcode, 1475 const gchar * function) 1476 { 1477 if (retcode == TRUE) 1478 { 1479 if (strncmp(buffback, "RPRT", 4) == 0) 1480 { 1481 sat_log_log(SAT_LOG_LEVEL_ERROR, 1482 _("%s:%s: %s rigctld returned error (%s)"), 1483 __FILE__, __func__, function, buffback); 1484 1485 retcode = FALSE; 1486 } 1487 } 1488 1489 return retcode; 1490 } 1491 1492 static int get_vfos(GtkRigCtrl * ctrl, char *rx, char *tx) 1493 { 1494 // fill rx/tx with vfo name plus space if not empty 1495 rx = tx = ""; 1496 switch (ctrl->conf->vfoUp) 1497 { 1498 case VFO_A: 1499 if (ctrl->conf->vfo_opt) 1500 {rx = "VFOB ";tx = "VFOA ";} 1501 break; 1502 1503 case VFO_B: 1504 if (ctrl->conf->vfo_opt) 1505 {rx = "VFOA ";tx = "VFOB ";} 1506 break; 1507 1508 case VFO_MAIN: 1509 if (ctrl->conf->vfo_opt) 1510 {rx = "Sub";tx = "Main";} 1511 break; 1512 1513 case VFO_SUB: 1514 if (ctrl->conf->vfo_opt) 1515 {rx = "Main";tx = "Sub";} 1516 break; 1517 1518 default: 1519 sat_log_log(SAT_LOG_LEVEL_ERROR, 1520 _("%s called but TX VFO is %d and we don't know how to handle it."), __func__, 1521 ctrl->conf->vfoUp); 1522 return 1; 1523 } 1524 sat_log_log(SAT_LOG_LEVEL_DEBUG, "rx=%x, tx=%s\n", rx, tx); 1525 return 0; 1526 } 1527 1528 /* Setup VFOs for split operation (simplex or duplex) */ 1529 static gboolean setup_split(GtkRigCtrl * ctrl) 1530 { 1531 gchar *buff; 1532 gchar buffback[256]; 1533 gboolean retcode; 1534 gchar *rx="", *tx=""; 1535 1536 get_vfos(ctrl, rx, tx); 1537 switch (ctrl->conf->vfoUp) 1538 { 1539 case VFO_A: 1540 if (ctrl->conf->vfo_opt) 1541 buff = g_strdup("S VFOB 1 VFOA\x0a"); 1542 else 1543 buff = g_strdup("S 1 VFOA\x0a"); 1544 break; 1545 1546 case VFO_B: 1547 if (ctrl->conf->vfo_opt) 1548 buff = g_strdup("S VFOA 1 VFOB\x0a"); 1549 else 1550 buff = g_strdup("S 1 VFOB\x0a"); 1551 break; 1552 1553 case VFO_MAIN: 1554 if (ctrl->conf->vfo_opt) 1555 buff = g_strdup("S Sub 1 Main\x0a"); 1556 else 1557 buff = g_strdup("S 1 Main\x0a"); 1558 break; 1559 1560 case VFO_SUB: 1561 if (ctrl->conf->vfo_opt) 1562 buff = g_strdup("S Main 1 Sub\x0a"); 1563 else 1564 buff = g_strdup("S 1 Sub\x0a"); 1565 break; 1566 1567 default: 1568 sat_log_log(SAT_LOG_LEVEL_ERROR, 1569 _("%s called but TX VFO is %d."), __func__, 1570 ctrl->conf->vfoUp); 1571 return FALSE; 1572 } 1573 1574 retcode = send_rigctld_command(ctrl, ctrl->sock, buff, buffback, 128); 1575 g_free(buff); 1576 1577 return (check_set_response(buffback, retcode, __func__)); 1578 } 1579 1580 static gboolean rig_ctrl_timeout_cb(gpointer data) 1581 { 1582 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 1583 1584 if (ctrl->conf == NULL) 1585 { 1586 sat_log_log(SAT_LOG_LEVEL_ERROR, 1587 _("%s: Controller does not have a valid configuration"), 1588 __func__); 1589 return FALSE; 1590 } 1591 1592 if (g_mutex_trylock(&(ctrl->busy)) == FALSE) 1593 { 1594 sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s missed the deadline"), 1595 __func__); 1596 return TRUE; 1597 } 1598 1599 setconfig(ctrl); 1600 g_mutex_unlock(&(ctrl->busy)); 1601 1602 return TRUE; 1603 } 1604 1605 static void exec_rx_cycle(GtkRigCtrl * ctrl) 1606 { 1607 gdouble readfreq = 0.0, tmpfreq, satfreqd, satfrequ; 1608 gboolean ptt = FALSE; 1609 1610 /* get PTT status */ 1611 if (ctrl->engaged && ctrl->conf->ptt) 1612 ptt = get_ptt(ctrl, ctrl->sock); 1613 1614 /* Dial feedback: 1615 If radio device is engaged read frequency from radio and compare it to the 1616 last set frequency. If different, it means that user has changed frequency 1617 on the radio dial => update transponder knob 1618 1619 Note: If ctrl->lastrxf = 0.0 the sync has been invalidated (e.g. user pressed "tune") 1620 and no need to execute the dial feedback. 1621 */ 1622 if ((ctrl->engaged) && (ctrl->lastrxf > 0.0) && (ptt == FALSE)) 1623 { 1624 if (!get_freq_simplex(ctrl, ctrl->sock, &readfreq)) 1625 { 1626 /* error => use a passive value */ 1627 ctrl->errcnt++; 1628 } 1629 else if (fabs(readfreq - ctrl->lastrxf) >= 1.0) 1630 { 1631 /* user might have altered radio frequency => update transponder knob */ 1632 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 1633 readfreq); 1634 ctrl->lastrxf = readfreq; 1635 1636 /* doppler shift; only if we are tracking */ 1637 if (ctrl->tracking) 1638 { 1639 satfreqd = (readfreq - ctrl->dd + ctrl->conf->lo); 1640 } 1641 else 1642 { 1643 satfreqd = readfreq + ctrl->conf->lo; 1644 } 1645 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqDown), 1646 satfreqd); 1647 1648 /* Update uplink if locked to downlink */ 1649 if (ctrl->trsplock) 1650 { 1651 track_downlink(ctrl); 1652 } 1653 1654 /* no need to forward track */ 1655 return; 1656 } 1657 } 1658 1659 /* now, forward tracking */ 1660 1661 /* If we are tracking, calculate the radio freq by applying both dopper shift 1662 and tranverter LO frequency. If we are not tracking, apply only LO frequency. 1663 */ 1664 satfreqd = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 1665 satfrequ = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 1666 if (ctrl->tracking) 1667 { 1668 /* downlink */ 1669 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 1670 satfreqd + ctrl->dd - ctrl->conf->lo); 1671 /* uplink */ 1672 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 1673 satfrequ + ctrl->du - ctrl->conf->loup); 1674 } 1675 else 1676 { 1677 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 1678 satfreqd - ctrl->conf->lo); 1679 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 1680 satfrequ - ctrl->conf->loup); 1681 } 1682 1683 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqDown)); 1684 1685 /* if device is engaged, send freq command to radio */ 1686 if ((ctrl->engaged) && (ptt == FALSE) && 1687 (fabs(ctrl->lastrxf - tmpfreq) >= 1.0)) 1688 { 1689 if (set_freq_simplex(ctrl, ctrl->sock, tmpfreq)) 1690 { 1691 /* reset error counter */ 1692 ctrl->errcnt = 0; 1693 1694 /* give radio a chance to set frequency */ 1695 g_usleep(WR_DEL); 1696 1697 /* The actual frequency might be different from what we have set because 1698 the tuning step is larger than what we work with (e.g. FT-817 has a 1699 smallest tuning step of 10 Hz). Therefore we read back the actual 1700 frequency from the rig. */ 1701 get_freq_simplex(ctrl, ctrl->sock, &tmpfreq); 1702 ctrl->lastrxf = tmpfreq; 1703 1704 /* This is only effective in RIG_TYPE_TRX mode. 1705 Invalidate ctrl->lasttxf for two reasons. 1706 1707 1. Prevent dial feedback from changing the uplink frequency. 1708 In the first TX cycle get_freq_simplex() returns the downlink 1709 frequency instead of uplink. The mismatch would thus trigger 1710 an uplink update as long as the VFO has not been updated. 1711 2. Force updating the VFO in the first TX cycle. 1712 */ 1713 if (ctrl->lastrxptt != ptt) 1714 ctrl->lasttxf = 0.0; 1715 } 1716 else 1717 { 1718 ctrl->errcnt++; 1719 } 1720 } 1721 1722 /* Remember PTT state, to avoid misinterpreting VFO changes as dial 1723 feedback during TX to RX transitions. 1724 */ 1725 ctrl->lastrxptt = ptt; 1726 } 1727 1728 static void exec_tx_cycle(GtkRigCtrl * ctrl) 1729 { 1730 gdouble readfreq = 0.0, tmpfreq, satfreqd, satfrequ; 1731 gboolean ptt = TRUE; 1732 1733 /* get PTT status */ 1734 if (ctrl->engaged && ctrl->conf->ptt) 1735 { 1736 ptt = get_ptt(ctrl, ctrl->sock); 1737 } 1738 1739 /* Dial feedback: 1740 If radio device is engaged read frequency from radio and compare it to the 1741 last set frequency. If different, it means that user has changed frequency 1742 on the radio dial => update transponder knob 1743 1744 Note: If ctrl->lasttxf = 0.0 the sync has been invalidated (e.g. user pressed "tune") 1745 and no need to execute the dial feedback. 1746 */ 1747 if ((ctrl->engaged) && (ctrl->lasttxf > 0.0) && (ptt == TRUE)) 1748 { 1749 if (!get_freq_simplex(ctrl, ctrl->sock, &readfreq)) 1750 { 1751 /* error => use a passive value */ 1752 ctrl->errcnt++; 1753 } 1754 else if (fabs(readfreq - ctrl->lasttxf) >= 1.0) 1755 { 1756 /* user might have altered radio frequency => update transponder knob */ 1757 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), readfreq); 1758 ctrl->lasttxf = readfreq; 1759 1760 /* doppler shift; only if we are tracking */ 1761 if (ctrl->tracking) 1762 { 1763 satfrequ = readfreq - ctrl->du + ctrl->conf->loup; 1764 } 1765 else 1766 { 1767 satfrequ = readfreq + ctrl->conf->loup; 1768 } 1769 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqUp), satfrequ); 1770 1771 /* Follow with downlink if transponder is locked */ 1772 if (ctrl->trsplock) 1773 { 1774 track_uplink(ctrl); 1775 } 1776 1777 /* no need to forward track */ 1778 return; 1779 } 1780 } 1781 1782 /* now, forward tracking */ 1783 1784 /* If we are tracking, calculate the radio freq by applying both dopper shift 1785 and tranverter LO frequency. If we are not tracking, apply only LO frequency. 1786 */ 1787 satfreqd = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 1788 satfrequ = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 1789 if (ctrl->tracking) 1790 { 1791 /* downlink */ 1792 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 1793 satfreqd + ctrl->dd - ctrl->conf->lo); 1794 /* uplink */ 1795 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 1796 satfrequ + ctrl->du - ctrl->conf->loup); 1797 } 1798 else 1799 { 1800 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 1801 satfreqd - ctrl->conf->lo); 1802 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 1803 satfrequ - ctrl->conf->loup); 1804 } 1805 1806 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqUp)); 1807 1808 /* if device is engaged, send freq command to radio */ 1809 if ((ctrl->engaged) && (ptt == TRUE) && 1810 (fabs(ctrl->lasttxf - tmpfreq) >= 1.0)) 1811 { 1812 if (set_freq_simplex(ctrl, ctrl->sock, tmpfreq)) 1813 { 1814 /* reset error counter */ 1815 ctrl->errcnt = 0; 1816 1817 /* give radio a chance to set frequency */ 1818 g_usleep(WR_DEL); 1819 1820 /* The actual frequency migh be different from what we have set because 1821 the tuning step is larger than what we work with (e.g. FT-817 has a 1822 smallest tuning step of 10 Hz). Therefore we read back the actual 1823 frequency from the rig. */ 1824 get_freq_simplex(ctrl, ctrl->sock, &tmpfreq); 1825 ctrl->lasttxf = tmpfreq; 1826 1827 /* This is only effective in RIG_TYPE_TRX mode. 1828 Invalidate ctrl->lastrxf for two reasons. 1829 1830 1. Prevent dial feedback from changing the downlink frequency. 1831 In the first RX cycle get_freq_simplex() returns the uplink 1832 frequency instead of downlink. The mismatch would thus 1833 trigger a downlink update as long as the VFO has not been 1834 updated. 1835 2. Force updating the VFO in the first RX cycle. 1836 */ 1837 if (ctrl->lasttxptt != ptt) 1838 ctrl->lastrxf = 0.0; 1839 } 1840 else 1841 { 1842 ctrl->errcnt++; 1843 } 1844 } 1845 1846 /* Remember PTT state, to avoid misinterpreting VFO changes as dial 1847 feedback during RX to TX transitions. 1848 */ 1849 ctrl->lasttxptt = ptt; 1850 } 1851 1852 static void exec_trx_cycle(GtkRigCtrl * ctrl) 1853 { 1854 exec_rx_cycle(ctrl); 1855 exec_tx_cycle(ctrl); 1856 } 1857 1858 static void exec_toggle_cycle(GtkRigCtrl * ctrl) 1859 { 1860 exec_rx_cycle(ctrl); 1861 1862 /* TX cycle is executed only if user selected RIG_TYPE_TOGGLE_AUTO 1863 * In manual mode the TX freq update is performed only when TX is activated. 1864 * Even in auto mode, the toggling is performed only once every 10 seconds. 1865 */ 1866 if (ctrl->conf->type == RIG_TYPE_TOGGLE_AUTO) 1867 { 1868 gint64 current_time; 1869 1870 /* get the current time */ 1871 current_time = g_get_real_time() / G_USEC_PER_SEC; 1872 1873 if ((ctrl->last_toggle_tx == -1) || 1874 ((current_time - ctrl->last_toggle_tx) >= 10)) 1875 { 1876 /* it's time to update TX freq */ 1877 exec_toggle_tx_cycle(ctrl); 1878 1879 /* store current time */ 1880 ctrl->last_toggle_tx = current_time; 1881 } 1882 } 1883 } 1884 1885 /* 1886 * Execute TX mode cycle. 1887 * 1888 * This function executes a transmit cycle when the primary device is of 1889 * RIG_TYPE_TOGGLE_AUTO. This applies to radios that support split operation 1890 * (e.g. TX on VHF, RX on UHF) where the frequency can not be set via CAT while 1891 * PTT is active. 1892 * 1893 * If PTT=TRUE we are in TX mode and hence there is nothing to do since the 1894 * frequency is kept constant. 1895 * 1896 * If PTT=FALSE we are in RX mode and we should update the TX frequency by 1897 * using set_freq_toggle() 1898 * 1899 * For these kind of radios there is no dial-feedback for the TX frequency. 1900 */ 1901 1902 static void exec_toggle_tx_cycle(GtkRigCtrl * ctrl) 1903 { 1904 gdouble tmpfreq; 1905 gboolean ptt = TRUE; 1906 1907 if (ctrl->engaged && ctrl->conf->ptt) 1908 { 1909 ptt = get_ptt(ctrl, ctrl->sock); 1910 } 1911 1912 /* if we are in TX mode do nothing */ 1913 if (ptt == TRUE) 1914 { 1915 return; 1916 } 1917 1918 /* Get the desired uplink frequency from controller */ 1919 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqUp)); 1920 1921 /* if device is engaged, send freq command to radio */ 1922 if ((ctrl->engaged) && (fabs(ctrl->lasttxf - tmpfreq) >= 10.0)) 1923 { 1924 if (set_freq_toggle(ctrl, ctrl->sock, tmpfreq)) 1925 { 1926 /* reset error counter */ 1927 ctrl->errcnt = 0; 1928 } 1929 else 1930 { 1931 ctrl->errcnt++; 1932 } 1933 1934 /* store the last sent frequency even if an error occurred */ 1935 ctrl->lasttxf = tmpfreq; 1936 } 1937 1938 } 1939 1940 static void exec_duplex_tx_cycle(GtkRigCtrl * ctrl) 1941 { 1942 gdouble readfreq = 0.0, tmpfreq, satfreqd, satfrequ; 1943 gboolean dialchanged = FALSE; 1944 1945 /* Dial feedback: 1946 If radio device is engaged read frequency from radio and compare it to the 1947 last set frequency. If different, it means that user has changed frequency 1948 on the radio dial => update transponder knob 1949 1950 Note: If ctrl->lasttxf = 0.0 the sync has been invalidated (e.g. user pressed "tune") 1951 and no need to execute the dial feedback. 1952 */ 1953 if ((ctrl->engaged) && (ctrl->lasttxf > 0.0)) 1954 { 1955 if (!get_freq_toggle(ctrl, ctrl->sock, &readfreq)) 1956 { 1957 /* error => use a passive value */ 1958 readfreq = ctrl->lasttxf; 1959 ctrl->errcnt++; 1960 } 1961 1962 if (fabs(readfreq - ctrl->lasttxf) >= 1.0) 1963 { 1964 dialchanged = TRUE; 1965 1966 /* user might have altered radio frequency => update transponder knob */ 1967 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), readfreq); 1968 ctrl->lasttxf = readfreq; 1969 1970 /* doppler shift; only if we are tracking */ 1971 if (ctrl->tracking) 1972 { 1973 satfrequ = readfreq - ctrl->du + ctrl->conf->loup; 1974 } 1975 else 1976 { 1977 satfrequ = readfreq + ctrl->conf->loup; 1978 } 1979 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqUp), satfrequ); 1980 1981 /* Follow with downlink if transponder is locked */ 1982 if (ctrl->trsplock) 1983 { 1984 track_uplink(ctrl); 1985 } 1986 } 1987 } 1988 1989 /* now, forward tracking */ 1990 if (dialchanged) 1991 { 1992 /* no need to forward track */ 1993 return; 1994 } 1995 1996 /* If we are tracking, calculate the radio freq by applying both dopper shift 1997 and tranverter LO frequency. If we are not tracking, apply only LO frequency. 1998 */ 1999 satfreqd = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 2000 satfrequ = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 2001 if (ctrl->tracking) 2002 { 2003 /* downlink */ 2004 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2005 satfreqd + ctrl->dd - ctrl->conf->lo); 2006 /* uplink */ 2007 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2008 satfrequ + ctrl->du - ctrl->conf->loup); 2009 } 2010 else 2011 { 2012 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2013 satfreqd - ctrl->conf->lo); 2014 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2015 satfrequ - ctrl->conf->loup); 2016 } 2017 2018 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqUp)); 2019 2020 /* if device is engaged, send freq command to radio */ 2021 if ((ctrl->engaged) && (fabs(ctrl->lasttxf - tmpfreq) >= 1.0)) 2022 { 2023 if (set_freq_toggle(ctrl, ctrl->sock, tmpfreq)) 2024 { 2025 /* reset error counter */ 2026 ctrl->errcnt = 0; 2027 2028 /* give radio a chance to set frequency */ 2029 g_usleep(WR_DEL); 2030 2031 /* The actual frequency migh be different from what we have set because 2032 the tuning step is larger than what we work with (e.g. FT-817 has a 2033 smallest tuning step of 10 Hz). Therefore we read back the actual 2034 frequency from the rig. */ 2035 get_freq_toggle(ctrl, ctrl->sock, &tmpfreq); 2036 ctrl->lasttxf = tmpfreq; 2037 } 2038 else 2039 { 2040 ctrl->errcnt++; 2041 } 2042 } 2043 } 2044 2045 static void exec_duplex_cycle(GtkRigCtrl * ctrl) 2046 { 2047 exec_rx_cycle(ctrl); 2048 exec_duplex_tx_cycle(ctrl); 2049 } 2050 2051 static void exec_dual_rig_cycle(GtkRigCtrl * ctrl) 2052 { 2053 gdouble tmpfreq, readfreq, satfreqd, satfrequ; 2054 gboolean dialchanged = FALSE; 2055 2056 /* Execute downlink cycle using ctrl->conf */ 2057 if (ctrl->engaged && (ctrl->lastrxf > 0.0)) 2058 { 2059 /* get frequency from receiver */ 2060 if (!get_freq_simplex(ctrl, ctrl->sock, &readfreq)) 2061 { 2062 /* error => use a passive value */ 2063 readfreq = ctrl->lastrxf; 2064 ctrl->errcnt++; 2065 } 2066 2067 if (fabs(readfreq - ctrl->lastrxf) >= 1.0) 2068 { 2069 dialchanged = TRUE; 2070 2071 /* user might have altered radio frequency => update transponder knob */ 2072 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2073 readfreq); 2074 ctrl->lastrxf = readfreq; 2075 2076 /* doppler shift; only if we are tracking */ 2077 if (ctrl->tracking) 2078 { 2079 satfreqd = readfreq - ctrl->dd + ctrl->conf->lo; 2080 } 2081 else 2082 { 2083 satfreqd = readfreq + ctrl->conf->lo; 2084 } 2085 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqDown), 2086 satfreqd); 2087 2088 /* Update uplink if locked to downlink */ 2089 if (ctrl->trsplock) 2090 { 2091 track_downlink(ctrl); 2092 } 2093 } 2094 } 2095 2096 if (dialchanged) 2097 { 2098 /* update uplink */ 2099 satfrequ = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 2100 if (ctrl->tracking) 2101 { 2102 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2103 satfrequ + ctrl->du - ctrl->conf2->loup); 2104 } 2105 else 2106 { 2107 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2108 satfrequ - ctrl->conf2->loup); 2109 } 2110 2111 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqUp)); 2112 2113 /* if device is engaged, send freq command to radio */ 2114 if ((ctrl->engaged) && (fabs(ctrl->lasttxf - tmpfreq) >= 1.0)) 2115 { 2116 if (set_freq_simplex(ctrl, ctrl->sock2, tmpfreq)) 2117 { 2118 /* reset error counter */ 2119 ctrl->errcnt = 0; 2120 2121 /* give radio a chance to set frequency */ 2122 g_usleep(WR_DEL); 2123 2124 /* The actual frequency migh be different from what we have set */ 2125 get_freq_simplex(ctrl, ctrl->sock2, &tmpfreq); 2126 ctrl->lasttxf = tmpfreq; 2127 } 2128 else 2129 { 2130 ctrl->errcnt++; 2131 } 2132 } 2133 } /* dialchanged on downlink */ 2134 else 2135 { 2136 /* if no dial change on downlink perform forward tracking on downlink 2137 and execute uplink controller too */ 2138 satfreqd = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 2139 if (ctrl->tracking) 2140 { 2141 /* downlink */ 2142 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2143 satfreqd + ctrl->dd - ctrl->conf->lo); 2144 } 2145 else 2146 { 2147 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2148 satfreqd - ctrl->conf->lo); 2149 } 2150 2151 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqDown)); 2152 2153 /* if device is engaged, send freq command to radio */ 2154 if ((ctrl->engaged) && (fabs(ctrl->lastrxf - tmpfreq) >= 1.0)) 2155 { 2156 if (set_freq_simplex(ctrl, ctrl->sock, tmpfreq)) 2157 { 2158 /* reset error counter */ 2159 ctrl->errcnt = 0; 2160 2161 /* give radio a chance to set frequency */ 2162 g_usleep(WR_DEL); 2163 2164 /* The actual frequency migh be different from what we have set */ 2165 get_freq_simplex(ctrl, ctrl->sock, &tmpfreq); 2166 ctrl->lastrxf = tmpfreq; 2167 } 2168 else 2169 { 2170 ctrl->errcnt++; 2171 } 2172 } 2173 2174 /* Now execute uplink controller */ 2175 2176 /* check if uplink dial has changed */ 2177 if ((ctrl->engaged) && (ctrl->lasttxf > 0.0)) 2178 { 2179 if (!get_freq_simplex(ctrl, ctrl->sock2, &readfreq)) 2180 { 2181 /* error => use a passive value */ 2182 readfreq = ctrl->lasttxf; 2183 ctrl->errcnt++; 2184 } 2185 2186 if (fabs(readfreq - ctrl->lasttxf) >= 1.0) 2187 { 2188 dialchanged = TRUE; 2189 2190 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2191 readfreq); 2192 ctrl->lasttxf = readfreq; 2193 2194 /* doppler shift; only if we are tracking */ 2195 if (ctrl->tracking) 2196 { 2197 satfrequ = readfreq - ctrl->du + ctrl->conf2->loup; 2198 } 2199 else 2200 { 2201 satfrequ = readfreq + ctrl->conf2->loup; 2202 } 2203 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->SatFreqUp), 2204 satfrequ); 2205 2206 /* Follow with downlink if transponder is locked */ 2207 if (ctrl->trsplock) 2208 { 2209 track_uplink(ctrl); 2210 } 2211 } 2212 } 2213 2214 if (dialchanged) 2215 { /* on uplink */ 2216 /* update downlink */ 2217 satfreqd = 2218 gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqDown)); 2219 if (ctrl->tracking) 2220 { 2221 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2222 satfreqd + ctrl->dd - ctrl->conf->lo); 2223 } 2224 else 2225 { 2226 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqDown), 2227 satfreqd - ctrl->conf->lo); 2228 } 2229 2230 tmpfreq = 2231 gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqDown)); 2232 2233 /* if device is engaged, send freq command to radio */ 2234 if ((ctrl->engaged) && (fabs(ctrl->lastrxf - tmpfreq) >= 1.0)) 2235 { 2236 if (set_freq_simplex(ctrl, ctrl->sock, tmpfreq)) 2237 { 2238 /* reset error counter */ 2239 ctrl->errcnt = 0; 2240 2241 /* give radio a chance to set frequency */ 2242 g_usleep(WR_DEL); 2243 2244 /* The actual frequency migh be different from what we have set */ 2245 get_freq_simplex(ctrl, ctrl->sock, &tmpfreq); 2246 ctrl->lastrxf = tmpfreq; 2247 } 2248 else 2249 { 2250 ctrl->errcnt++; 2251 } 2252 } 2253 } /* dialchanged on uplink */ 2254 else 2255 { 2256 /* perform forward tracking on uplink */ 2257 satfrequ = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->SatFreqUp)); 2258 if (ctrl->tracking) 2259 { 2260 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2261 satfrequ + ctrl->du - 2262 ctrl->conf2->loup); 2263 } 2264 else 2265 { 2266 gtk_freq_knob_set_value(GTK_FREQ_KNOB(ctrl->RigFreqUp), 2267 satfrequ - ctrl->conf2->loup); 2268 } 2269 2270 tmpfreq = gtk_freq_knob_get_value(GTK_FREQ_KNOB(ctrl->RigFreqUp)); 2271 2272 /* if device is engaged, send freq command to radio */ 2273 if ((ctrl->engaged) && (fabs(ctrl->lasttxf - tmpfreq) >= 1.0)) 2274 { 2275 if (set_freq_simplex(ctrl, ctrl->sock2, tmpfreq)) 2276 { 2277 /* reset error counter */ 2278 ctrl->errcnt = 0; 2279 2280 /* give radio a chance to set frequency */ 2281 g_usleep(WR_DEL); 2282 2283 /* The actual frequency might be different from what we have set. */ 2284 get_freq_simplex(ctrl, ctrl->sock2, &tmpfreq); 2285 ctrl->lasttxf = tmpfreq; 2286 } 2287 else 2288 { 2289 ctrl->errcnt++; 2290 } 2291 } 2292 } /* else dialchange on uplink */ 2293 } /* else dialchange on downlink */ 2294 } 2295 2296 static gboolean get_ptt(GtkRigCtrl * ctrl, gint sock) 2297 { 2298 gchar *buff, **vbuff; 2299 gchar buffback[128]; 2300 gboolean retcode; 2301 guint64 pttstat = 0; 2302 2303 if (ctrl->conf->ptt == PTT_TYPE_CAT) 2304 { 2305 /* send command get_ptt (t) */ 2306 if (ctrl->conf->vfo_opt) 2307 buff = g_strdup_printf("t currVFO\x0a"); 2308 else 2309 buff = g_strdup_printf("t\x0a"); 2310 } 2311 else 2312 { 2313 /* send command \get_dcd */ 2314 if (ctrl->conf->vfo_opt) 2315 buff = g_strdup_printf("%c currVFO\x0a", 0x8b); 2316 else 2317 buff = g_strdup_printf("%c\x0a", 0x8b); 2318 } 2319 2320 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2321 if (retcode) 2322 { 2323 vbuff = g_strsplit(buffback, "\n", 3); 2324 if (vbuff[0]) 2325 pttstat = g_ascii_strtoull(vbuff[0], NULL, 0); //FIXME base = 0 ok? 2326 g_strfreev(vbuff); 2327 } 2328 2329 g_free(buff); 2330 2331 return (pttstat == 1) ? TRUE : FALSE; 2332 } 2333 2334 static gboolean set_ptt(GtkRigCtrl * ctrl, gint sock, gboolean ptt) 2335 { 2336 gchar *buff; 2337 gchar buffback[128]; 2338 gboolean retcode; 2339 2340 /* send command */ 2341 if (ptt == TRUE) 2342 { 2343 if (ctrl->conf->vfo_opt) 2344 buff = g_strdup_printf("T currVFO 1\x0aq\x0a"); 2345 else 2346 buff = g_strdup_printf("T 1\x0aq\x0a"); 2347 } 2348 else 2349 { 2350 if (ctrl->conf->vfo_opt) 2351 buff = g_strdup_printf("T currVFO 0\x0aq\x0a"); 2352 else 2353 buff = g_strdup_printf("T 0\x0aq\x0a"); 2354 } 2355 2356 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2357 g_free(buff); 2358 2359 return (check_set_response(buffback, retcode, __func__)); 2360 2361 } 2362 2363 /* 2364 * Check for AOS and LOS and send signal if enabled for rig. 2365 * 2366 * @param ctrl Pointer to the GtkRigCtrl handle. 2367 * @return TRUE if the operation was successful, FALSE if a connection error 2368 * occurred. 2369 * 2370 * This function checks whether AOS or LOS just happened and sends the 2371 * appropriate signal to the RIG if this signalling is enabled. 2372 */ 2373 static gboolean check_aos_los(GtkRigCtrl * ctrl) 2374 { 2375 gboolean retcode = TRUE; 2376 gchar retbuf[10]; 2377 2378 if (ctrl->engaged && ctrl->tracking) 2379 { 2380 if (ctrl->prev_ele < 0.0 && ctrl->target->el >= 0.0) 2381 { 2382 /* AOS has occurred */ 2383 if (ctrl->conf->signal_aos) 2384 { 2385 retcode &= send_rigctld_command(ctrl, ctrl->sock, "AOS\n", 2386 retbuf, 10); 2387 } 2388 if (ctrl->conf2 != NULL) 2389 { 2390 if (ctrl->conf2->signal_aos) 2391 { 2392 retcode &= send_rigctld_command(ctrl, ctrl->sock2, "AOS\n", 2393 retbuf, 10); 2394 } 2395 } 2396 } 2397 else if (ctrl->prev_ele >= 0.0 && ctrl->target->el < 0.0) 2398 { 2399 /* LOS has occurred */ 2400 if (ctrl->conf->signal_los) 2401 { 2402 retcode &= send_rigctld_command(ctrl, ctrl->sock, "LOS\n", 2403 retbuf, 10); 2404 } 2405 if (ctrl->conf2 != NULL) 2406 { 2407 if (ctrl->conf2->signal_los) 2408 { 2409 retcode &= send_rigctld_command(ctrl, ctrl->sock2, "LOS\n", 2410 retbuf, 10); 2411 } 2412 } 2413 } 2414 } 2415 2416 ctrl->prev_ele = ctrl->target->el; 2417 2418 return retcode; 2419 } 2420 2421 /* 2422 * Set frequency in simplex mode 2423 * 2424 * Returns TRUE if the operation was successful, FALSE otherwise 2425 */ 2426 static gboolean set_freq_simplex(GtkRigCtrl * ctrl, gint sock, gdouble freq) 2427 { 2428 gchar *buff; 2429 gchar buffback[128]; 2430 gboolean retcode; 2431 2432 if (ctrl->conf->vfo_opt) 2433 buff = g_strdup_printf("F currVFO %10.0f\x0a", freq); 2434 else 2435 buff = g_strdup_printf("F %10.0f\x0a", freq); 2436 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2437 g_free(buff); 2438 2439 return (check_set_response(buffback, retcode, __func__)); 2440 } 2441 2442 2443 /* 2444 * Set frequency in toggle mode 2445 * 2446 * Returns TRUE if the operation was successful, FALSE otherwise 2447 */ 2448 static gboolean set_freq_toggle(GtkRigCtrl * ctrl, gint sock, gdouble freq) 2449 { 2450 gchar *buff; 2451 gchar buffback[128]; 2452 gboolean retcode; 2453 2454 /* send command */ 2455 printf("set_freq_toggle %d\n", ctrl->conf->vfo_opt); 2456 if (ctrl->conf->vfo_opt) 2457 buff = g_strdup_printf("I VFOA %10.0f\x0a", freq); 2458 else 2459 buff = g_strdup_printf("I %10.0f\x0a", freq); 2460 2461 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2462 g_free(buff); 2463 2464 return (check_set_response(buffback, retcode, __func__)); 2465 2466 } 2467 2468 /* 2469 * Turn on the radios toggle mode 2470 * 2471 * Returns TRUE if the operation was successful 2472 */ 2473 static gboolean set_toggle(GtkRigCtrl * ctrl, gint sock) 2474 { 2475 gchar *buff; 2476 gchar buffback[128]; 2477 gboolean retcode; 2478 2479 if (ctrl->conf->vfo_opt) 2480 buff = g_strdup_printf("S %s 1 %d\x0a", ctrl->conf->vfoDown==VFO_A?"VFOA":"VFOB", ctrl->conf->vfoDown); 2481 else 2482 buff = g_strdup_printf("S 1 %d\x0a", ctrl->conf->vfoDown); 2483 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2484 g_free(buff); 2485 2486 return (check_set_response(buffback, retcode, __func__)); 2487 } 2488 2489 /* 2490 * Turn off the radios toggle mode 2491 * 2492 * Returns TRUE if the operation was successful 2493 */ 2494 static gboolean unset_toggle(GtkRigCtrl * ctrl, gint sock) 2495 { 2496 gchar *buff; 2497 gchar buffback[128]; 2498 gboolean retcode; 2499 2500 /* send command */ 2501 if (ctrl->conf->vfo_opt) 2502 buff = g_strdup_printf("S VFOA 0 %d\x0a", ctrl->conf->vfoDown); 2503 else 2504 buff = g_strdup_printf("S 0 %d\x0a", ctrl->conf->vfoDown); 2505 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2506 g_free(buff); 2507 2508 return (check_set_response(buffback, retcode, __func__)); 2509 } 2510 2511 /* 2512 * Get frequency 2513 * 2514 * Returns TRUE if the operation was successful, FALSE otherwise 2515 */ 2516 static gboolean get_freq_simplex(GtkRigCtrl * ctrl, gint sock, gdouble * freq) 2517 { 2518 gchar *buff, **vbuff; 2519 gchar buffback[128]; 2520 gboolean retcode; 2521 gboolean retval = TRUE; 2522 2523 if (ctrl->conf->vfo_opt) 2524 buff = g_strdup_printf("f currVFO\x0a"); 2525 else 2526 buff = g_strdup_printf("f\x0a"); 2527 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2528 retcode = check_get_response(buffback, retcode, __func__); 2529 if (retcode) 2530 { 2531 vbuff = g_strsplit(buffback, "\n", 3); 2532 if (vbuff[0]) 2533 *freq = g_ascii_strtod(vbuff[0], NULL); 2534 else 2535 retval = FALSE; 2536 g_strfreev(vbuff); 2537 } 2538 else 2539 { 2540 retval = FALSE; 2541 } 2542 2543 g_free(buff); 2544 return retval; 2545 } 2546 2547 /* 2548 * Get vfo option 2549 * 2550 * Returns TRUE if the vfo option enabled was successful, FALSE otherwise 2551 */ 2552 static gboolean get_vfo_opt(GtkRigCtrl * ctrl, gint sock) 2553 { 2554 gchar *buff; 2555 gchar buffback[128]; 2556 gboolean retcode; 2557 gboolean retval = TRUE; 2558 2559 buff = g_strdup_printf("\\set_vfo_opt 1\x0a"); 2560 send_rigctld_command(ctrl, sock, buff, buffback, 128); 2561 // we don't really care about the return from set_vto_opt 2562 // we'll check to see if it worked next 2563 buff = g_strdup_printf("\\chk_vfo\x0a"); 2564 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2565 retcode = check_get_response(buffback, retcode, __func__); 2566 if (retcode) 2567 { 2568 if (buffback[0]=='1') return TRUE; 2569 else return FALSE; 2570 } 2571 else 2572 { 2573 retval = FALSE; 2574 } 2575 2576 g_free(buff); 2577 return retval; 2578 } 2579 2580 /* 2581 * Get frequency when the radio is working toggle 2582 * 2583 * Returns TRUE if the operation was successful, FALSE otherwise 2584 */ 2585 static gboolean get_freq_toggle(GtkRigCtrl * ctrl, gint sock, gdouble * freq) 2586 { 2587 gchar *buff, **vbuff; 2588 gchar buffback[128]; 2589 gboolean retcode; 2590 gboolean retval = TRUE; 2591 2592 if (freq == NULL) 2593 { 2594 sat_log_log(SAT_LOG_LEVEL_ERROR, 2595 _("%s:%d: NULL storage."), __FILE__, __LINE__); 2596 return FALSE; 2597 } 2598 2599 /* send command */ 2600 if (ctrl->conf->vfo_opt) 2601 buff = g_strdup_printf("i currVFO\x0a"); 2602 else 2603 buff = g_strdup_printf("i\x0a"); 2604 retcode = send_rigctld_command(ctrl, sock, buff, buffback, 128); 2605 retcode = check_get_response(buffback, retcode, __func__); 2606 if (retcode) 2607 { 2608 vbuff = g_strsplit(buffback, "\n", 3); 2609 if (vbuff[0]) 2610 *freq = g_ascii_strtod(vbuff[0], NULL); 2611 else 2612 retval = FALSE; 2613 2614 g_strfreev(vbuff); 2615 } 2616 else 2617 { 2618 retval = FALSE; 2619 } 2620 2621 g_free(buff); 2622 return retval; 2623 } 2624 2625 /* 2626 * This function is used to manage PTT events, e.g. the user presses 2627 * the spacebar. It is only useful for RIG_TYPE_TOGGLE_MAN and possibly for 2628 * RIG_TYPE_TOGGLE_AUTO. 2629 * 2630 * First, the function will try to lock the controller. If the lock is 2631 * acquired the function checks the current PTT status. 2632 * If PTT status is FALSE (off), it will set the TX frequency and set PTT to 2633 * TRUE (on). If PTT status is TRUE (on) it will simply set the PTT to FALSE 2634 * (off). 2635 * 2636 * This function assumes that the radio support set/get PTT, otherwise it makes 2637 * no sense to use it! 2638 */ 2639 static void manage_ptt_event(GtkRigCtrl * ctrl) 2640 { 2641 guint timeout = 1; 2642 gboolean ptt = FALSE; 2643 2644 /* wait for controller to be idle or until the timeout triggers */ 2645 while (timeout < 5) 2646 { 2647 if (g_mutex_trylock(&(ctrl->busy)) == TRUE) 2648 { 2649 timeout = 17; /* use an arbitrary value that is large enough */ 2650 } 2651 else 2652 { 2653 /* wait for 100 msec */ 2654 g_usleep(100000); 2655 timeout++; 2656 } 2657 } 2658 2659 if (timeout == 17) 2660 { 2661 /* timeout did not expire, we've got the controller lock */ 2662 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2663 _("%s: Acquired controller lock"), __func__); 2664 2665 if (ctrl->engaged == FALSE) 2666 { 2667 sat_log_log(SAT_LOG_LEVEL_INFO, 2668 _("%s: Controller not engaged; PTT event ignored " 2669 "(Hint: Enable the Engage button)"), __func__); 2670 } 2671 else 2672 { 2673 ptt = get_ptt(ctrl, ctrl->sock); 2674 2675 if (ptt == FALSE) 2676 { 2677 /* PTT is OFF => set TX freq then set PTT to ON */ 2678 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2679 _("%s: PTT is OFF => Set TX freq and PTT=ON"), 2680 __func__); 2681 2682 exec_toggle_tx_cycle(ctrl); 2683 set_ptt(ctrl, ctrl->sock, TRUE); 2684 } 2685 else 2686 { 2687 /* PTT is ON => set to OFF */ 2688 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2689 _("%s: PTT is ON = Set PTT=OFF"), __func__); 2690 2691 set_ptt(ctrl, ctrl->sock, FALSE); 2692 } 2693 } 2694 2695 g_mutex_unlock(&(ctrl->busy)); 2696 } 2697 else 2698 { 2699 sat_log_log(SAT_LOG_LEVEL_ERROR, 2700 _("%s: Failed to acquire controller lock; PTT event " 2701 "not handled"), __func__); 2702 } 2703 2704 } 2705 2706 2707 /* 2708 * Catch events when the user presses the SPACE key on the keyboard. 2709 * This is used to toggle betweer RX/TX when using FT817/857/897 in manual mode. 2710 */ 2711 static gboolean key_press_cb(GtkWidget * widget, GdkEventKey * pKey, 2712 gpointer data) 2713 { 2714 GtkRigCtrl *ctrl = GTK_RIG_CTRL(widget); 2715 gboolean event_managed = FALSE; 2716 2717 (void)data; 2718 2719 if (pKey->type == GDK_KEY_PRESS) 2720 { 2721 switch (pKey->keyval) 2722 { 2723 /* keyvals not in API docs. See <gdk/gdkkeysyms.h> for a complete list */ 2724 case GDK_KEY_space: 2725 sat_log_log(SAT_LOG_LEVEL_INFO, 2726 _("%s: Detected SPACEBAR pressed event"), __func__); 2727 2728 /* manage PTT event but only if rig is of type TOGGLE_MAN */ 2729 if (ctrl->conf->type == RIG_TYPE_TOGGLE_MAN) 2730 { 2731 manage_ptt_event(ctrl); 2732 event_managed = TRUE; 2733 } 2734 break; 2735 2736 default: 2737 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2738 _ 2739 ("%s:%s: Keypress value %i not managed by this function"), 2740 __FILE__, __func__, pKey->keyval); 2741 break; 2742 } 2743 } 2744 2745 return event_managed; 2746 } 2747 2748 static gboolean open_rigctld_socket(radio_conf_t * conf, gint * sock) 2749 { 2750 struct sockaddr_in ServAddr; 2751 struct hostent *h; 2752 gint status; 2753 2754 *sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 2755 if (*sock < 0) 2756 { 2757 sat_log_log(SAT_LOG_LEVEL_ERROR, 2758 _("%s: Failed to create socket"), __func__); 2759 *sock = 0; 2760 return FALSE; 2761 } 2762 else 2763 { 2764 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2765 _("%s: Network socket created successfully"), __func__); 2766 } 2767 2768 memset(&ServAddr, 0, sizeof(ServAddr)); /* Zero out structure */ 2769 ServAddr.sin_family = AF_INET; /* Internet address family */ 2770 h = gethostbyname(conf->host); 2771 memcpy((char *)&ServAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); 2772 ServAddr.sin_port = htons(conf->port); /* Server port */ 2773 2774 /* establish connection */ 2775 status = connect(*sock, (struct sockaddr *)&ServAddr, sizeof(ServAddr)); 2776 if (status < 0) 2777 { 2778 sat_log_log(SAT_LOG_LEVEL_ERROR, 2779 _("%s: Failed to connect to %s:%d"), 2780 __func__, conf->host, conf->port); 2781 *sock = 0; 2782 return FALSE; 2783 } 2784 else 2785 { 2786 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2787 _("%s: Connection opened to %s:%d"), 2788 __func__, conf->host, conf->port); 2789 } 2790 2791 return TRUE; 2792 } 2793 2794 static gboolean close_rigctld_socket(gint * sock) 2795 { 2796 gint written; 2797 2798 written = send(*sock, "q\x0a", 2, 0); 2799 if (written != 2) 2800 { 2801 sat_log_log(SAT_LOG_LEVEL_ERROR, 2802 _("%s:%s: Sent 2 bytes but sent %d."), 2803 __FILE__, __func__, written); 2804 } 2805 #ifndef WIN32 2806 shutdown(*sock, SHUT_RDWR); 2807 close(*sock); 2808 #else 2809 shutdown(*sock, SD_BOTH); 2810 closesocket(*sock); 2811 #endif 2812 2813 *sock = 0; 2814 2815 return TRUE; 2816 } 2817 2818 static void rigctrl_close(GtkRigCtrl * data) 2819 { 2820 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 2821 2822 ctrl->lastrxptt = FALSE; 2823 ctrl->lasttxptt = TRUE; 2824 ctrl->lasttxf = 0.0; 2825 ctrl->lastrxf = 0.0; 2826 2827 remove_timer(ctrl); 2828 2829 if ((ctrl->conf->type == RIG_TYPE_TOGGLE_AUTO) || 2830 (ctrl->conf->type == RIG_TYPE_TOGGLE_MAN)) 2831 { 2832 unset_toggle(ctrl, ctrl->sock); 2833 } 2834 2835 if (ctrl->conf2 != NULL) 2836 { 2837 close_rigctld_socket(&(ctrl->sock2)); 2838 } 2839 close_rigctld_socket(&(ctrl->sock)); 2840 } 2841 2842 static void rigctrl_open(GtkRigCtrl * data) 2843 { 2844 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 2845 2846 ctrl->wrops = 0; 2847 2848 start_timer(ctrl); 2849 2850 open_rigctld_socket(ctrl->conf, &(ctrl->sock)); 2851 2852 // check to see if vfo option is enabled 2853 ctrl->conf->vfo_opt = get_vfo_opt(ctrl, ctrl->sock); 2854 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2855 _("%s:%s: VFO opt=%d"), __FILE__, 2856 __func__, ctrl->conf->vfo_opt); 2857 2858 /* set initial frequency */ 2859 if (ctrl->conf2 != NULL) 2860 { 2861 open_rigctld_socket(ctrl->conf2, &(ctrl->sock2)); 2862 /* set initial dual mode */ 2863 ctrl->conf2->vfo_opt = get_vfo_opt(ctrl, ctrl->sock); 2864 sat_log_log(SAT_LOG_LEVEL_DEBUG, 2865 _("%s:%s: VFO opt2=%d"), __FILE__, 2866 __func__, ctrl->conf2->vfo_opt); 2867 2868 exec_dual_rig_cycle(ctrl); 2869 } 2870 else 2871 { 2872 switch (ctrl->conf->type) 2873 { 2874 2875 case RIG_TYPE_RX: 2876 exec_rx_cycle(ctrl); 2877 break; 2878 2879 case RIG_TYPE_TX: 2880 exec_tx_cycle(ctrl); 2881 break; 2882 2883 case RIG_TYPE_TRX: 2884 exec_trx_cycle(ctrl); 2885 break; 2886 2887 case RIG_TYPE_DUPLEX: 2888 /* set rig into SAT mode (hamlib needs it even if rig already in SAT) */ 2889 setup_split(ctrl); 2890 exec_duplex_cycle(ctrl); 2891 break; 2892 2893 case RIG_TYPE_TOGGLE_AUTO: 2894 case RIG_TYPE_TOGGLE_MAN: 2895 set_toggle(ctrl, ctrl->sock); 2896 ctrl->last_toggle_tx = -1; 2897 exec_toggle_cycle(ctrl); 2898 break; 2899 2900 default: 2901 /* this is an error! */ 2902 ctrl->conf->type = RIG_TYPE_RX; 2903 exec_rx_cycle(ctrl); 2904 break; 2905 } 2906 } 2907 } 2908 2909 /* Communication thread for hamlib rigctld */ 2910 gpointer rigctl_run(gpointer data) 2911 { 2912 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 2913 GtkRigCtrl *t_ctrl = GTK_RIG_CTRL(data); 2914 2915 while (1) 2916 { 2917 t_ctrl = GTK_RIG_CTRL(g_async_queue_pop(ctrl->rigctlq)); 2918 ctrl = t_ctrl; 2919 while (g_main_context_iteration(NULL, FALSE)); 2920 2921 if (t_ctrl == NULL) 2922 { 2923 sat_log_log(SAT_LOG_LEVEL_ERROR, 2924 _("%s:%s: ERROR: NO VALID ctrl-struct"), __FILE__, 2925 __func__); 2926 continue; 2927 } 2928 2929 if (t_ctrl->engaged) 2930 { 2931 if (!t_ctrl->sock) 2932 rigctrl_open(t_ctrl); 2933 2934 if (!t_ctrl->timerid) 2935 start_timer(t_ctrl); 2936 } 2937 else 2938 { 2939 g_mutex_lock(&t_ctrl->widgetsync); 2940 2941 if (t_ctrl->sock > 0) 2942 rigctrl_close(t_ctrl); 2943 2944 if (t_ctrl->timerid) 2945 remove_timer(t_ctrl); 2946 2947 g_cond_signal(&t_ctrl->widgetready); 2948 g_mutex_unlock(&t_ctrl->widgetsync); 2949 break; 2950 } 2951 2952 check_aos_los(t_ctrl); 2953 2954 if (t_ctrl->conf2 != NULL) 2955 { 2956 exec_dual_rig_cycle(t_ctrl); 2957 } 2958 else 2959 { 2960 /* Execute controller cycle depending on primary radio type */ 2961 switch (t_ctrl->conf->type) 2962 { 2963 2964 case RIG_TYPE_RX: 2965 exec_rx_cycle(t_ctrl); 2966 break; 2967 2968 case RIG_TYPE_TX: 2969 exec_tx_cycle(t_ctrl); 2970 break; 2971 2972 case RIG_TYPE_TRX: 2973 exec_trx_cycle(t_ctrl); 2974 break; 2975 2976 case RIG_TYPE_DUPLEX: 2977 exec_duplex_cycle(t_ctrl); 2978 break; 2979 2980 case RIG_TYPE_TOGGLE_AUTO: 2981 case RIG_TYPE_TOGGLE_MAN: 2982 exec_toggle_cycle(t_ctrl); 2983 break; 2984 2985 default: 2986 /* invalid mode */ 2987 sat_log_log(SAT_LOG_LEVEL_ERROR, 2988 _("%s:%s: Invalid radio type %d. Setting type to " 2989 "RIG_TYPE_RX"), __FILE__, __func__, 2990 t_ctrl->conf->type); 2991 t_ctrl->conf->type = RIG_TYPE_RX; 2992 } 2993 } 2994 2995 /* perform error count checking */ 2996 if (t_ctrl->errcnt >= MAX_ERROR_COUNT) 2997 { 2998 /* disengage device */ 2999 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_ctrl->LockBut), 3000 FALSE); 3001 t_ctrl->engaged = FALSE; 3002 t_ctrl->errcnt = 0; 3003 sat_log_log(SAT_LOG_LEVEL_ERROR, 3004 _ 3005 ("%s:%s: MAX_ERROR_COUNT (%d) reached. Disengaging device!"), 3006 __FILE__, __func__, MAX_ERROR_COUNT); 3007 3008 //g_print ("ERROR. WROPS = %d\n", ctrl->wrops); 3009 } 3010 3011 //g_print (" WROPS = %d\n", ctrl->wrops); 3012 } 3013 3014 if (t_ctrl->sock > 0) 3015 rigctrl_close(t_ctrl); 3016 3017 if (t_ctrl->timerid) 3018 remove_timer(t_ctrl); 3019 3020 return NULL; 3021 } 3022 3023 void start_timer(GtkRigCtrl * data) 3024 { 3025 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 3026 3027 /* start timeout timer here ("Cycle")! */ 3028 if (ctrl->timerid > 0) 3029 g_source_remove(ctrl->timerid); 3030 3031 ctrl->timerid = 3032 gdk_threads_add_timeout(ctrl->delay, rig_ctrl_timeout_cb, ctrl); 3033 } 3034 3035 void remove_timer(GtkRigCtrl * data) 3036 { 3037 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 3038 3039 /* stop timer */ 3040 if (ctrl->timerid > 0) 3041 g_source_remove(ctrl->timerid); 3042 ctrl->timerid = 0; 3043 } 3044 3045 void setconfig(gpointer data) 3046 { 3047 /* something has changed... */ 3048 GtkRigCtrl *ctrl = GTK_RIG_CTRL(data); 3049 3050 if (ctrl != NULL) 3051 { 3052 g_async_queue_push(ctrl->rigctlq, ctrl); 3053 } 3054 } 3055 3056 3057 GtkWidget *gtk_rig_ctrl_new(GtkSatModule * module) 3058 { 3059 GtkRigCtrl *rigctrl; 3060 GtkWidget *widget; 3061 GtkWidget *table; 3062 3063 if (!have_conf()) 3064 return NULL; 3065 3066 widget = g_object_new(GTK_TYPE_RIG_CTRL, NULL); 3067 rigctrl = GTK_RIG_CTRL(widget); 3068 3069 g_signal_connect(widget, "key-press-event", G_CALLBACK(key_press_cb), 3070 NULL); 3071 3072 g_hash_table_foreach(module->satellites, store_sats, widget); 3073 GTK_RIG_CTRL(widget)->target = SAT(g_slist_nth_data(rigctrl->sats, 0)); 3074 3075 rigctrl->qth = module->qth; 3076 3077 if (rigctrl->target != NULL) 3078 { 3079 /* get next pass for target satellite */ 3080 GTK_RIG_CTRL(widget)->pass = get_next_pass(rigctrl->target, 3081 rigctrl->qth, 3.0); 3082 } 3083 3084 /* create contents */ 3085 table = gtk_grid_new(); 3086 gtk_grid_set_row_spacing(GTK_GRID(table), 5); 3087 gtk_grid_set_column_spacing(GTK_GRID(table), 5); 3088 gtk_container_set_border_width(GTK_CONTAINER(table), 10); 3089 gtk_grid_attach(GTK_GRID(table), create_downlink_widgets(rigctrl), 3090 0, 0, 1, 1); 3091 gtk_grid_attach(GTK_GRID(table), create_uplink_widgets(rigctrl), 3092 1, 0, 1, 1); 3093 gtk_grid_attach(GTK_GRID(table), create_target_widgets(rigctrl), 3094 0, 1, 1, 1); 3095 gtk_grid_attach(GTK_GRID(table), create_conf_widgets(rigctrl), 1, 1, 1, 1); 3096 gtk_grid_attach(GTK_GRID(table), create_count_down_widgets(rigctrl), 3097 0, 2, 2, 1); 3098 3099 gtk_container_add(GTK_CONTAINER(rigctrl), table); 3100 3101 if (module->target > 0) 3102 gtk_rig_ctrl_select_sat(rigctrl, module->target); 3103 3104 return widget; 3105 }