/ src / gtk-rig-ctrl.c
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                                                     &gtk_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  }