Radio uplink and downlink frequencies are hard-coded, making ground station software automation difficult
Problem statement
At the Laboratory for Atmospheric and Space Physics (LASP) at the University of Colorado Boulder (CU), we use gpredict to control Doppler shift of our GNU Radio-based UHF transceiver and S-band receiver in our ground station. We also use gpredict for control of our UHF antenna rotator. (Our s-band dish uses its own proprietary software for tracking.) We use a set of home-grown shell and Python scripts to automate launching gpredict and our GNU Radio flowgraph (and some other internal software) a few minutes before each pass, but some human interaction is required to click buttons in the gpredict interface that are not currently managed in the gpredict config files (i.e., those which reset to default values when the software is restarted).
We would like if this automation software could be entirely autonomous—that is to say, not requiring any human interaction whatsoever.
One issue with gpredict as it currently stands is the rig control window default transmit and receive frequencies are initialized to a hard-coded value (145.890 MHz): there is no way to configure this value differently without modifying the source code and building a new executable. Ideally, this would be saved in the per-radio .rig configuration files.
Summary of existing code
The rig control window as of the latest version of gpredict appears as follows:
The widgets for adjusting the radio frequency are implemented in gtk-freq-knob.c and gtk-freq-knob.h. These widgets are instantiated in gtk-rig-ctrl.c in the create_uplink_widgets() and create_downlink_widgets() routines—on the downlink side, this looks like the following:
https://github.com/csete/gpredict/blob/a0a563684702ad4d1de6e128ca9219d6ee9daa47/src/gtk-rig-ctrl.c#L463-L467
The uplink side is similar. The currently-in-use frequencies displayed below these widgets is likewise instantiated in said routines, and also uses this 145890000.0 "magic number":
https://github.com/csete/gpredict/blob/a0a563684702ad4d1de6e128ca9219d6ee9daa47/src/gtk-rig-ctrl.c#L489-L496
Screenshots of modified program
Here are screenshots showing what the rig list and rig editor windows look like after the changes described below are implemented:


Summary of proposed code changes
It seems like a useful modification to gpredict would be to add default uplink and downlink frequecies to the rig config file (.rig). This requires the following changes:
- Add new member variables to the
radio_conf_tstruct inradio-conf.hto provide runtime storage of the default per-radio uplink and downlink frequencies - Add code to
radio_conf_read()inradio-conf.cto read default uplink and downlink frequencies from the.rigconfig files - Add code to
radio_conf_save()inradio-conf.cto save default uplink and downlink frequencies to the.rigconfig files - Add UI controls to the rig editor window to allow viewing and changing the default uplink and downlink frequencies
- Add columns to the rig list to display the default uplink and downlink frequencies in their own columns
Implementation
Adding configuration file options
Two new variables are added to the radio_conf_t struct in radio-conf.h, defUpFreq and defDnFreq:
diff --git a/src/radio-conf.h b/src/radio-conf.h
index a5c3aa5..a8b9578 100644
--- a/src/radio-conf.h
+++ b/src/radio-conf.h
@@ -65,6 +65,8 @@ typedef struct {
gdouble lo; /*!< local oscillator freq in Hz (using double for
compatibility with rest of code). Downlink. */
gdouble loup; /*!< local oscillator freq in Hz for uplink. */
+ gdouble defUpFreq; /*!< Default uplink in Hertz */
+ gdouble defDnFreq; /*!< Default downlink frequency in Hertz */
rig_type_t type; /*!< Radio type */
ptt_type_t ptt; /*!< PTT type (needed for RX, TX, and TRX) */
vfo_t vfoDown; /*!< Downlink VFO for full-duplex radios */
Next, keys for these options are added to radio-conf.c. These will be used later:
diff --git a/src/radio-conf.c b/src/radio-conf.c
index 46c70c5..2dc3954 100644
--- a/src/radio-conf.c
+++ b/src/radio-conf.c
@@ -39,6 +39,8 @@
#define KEY_CYCLE "Cycle"
#define KEY_LO "LO"
#define KEY_LOUP "LO_UP"
+#define KEY_DEF_UP_FREQ "DefaultUplinkFreqHz"
+#define KEY_DEF_DN_FREQ "DefaultDownlinkFreqHz"
#define KEY_TYPE "Type"
#define KEY_PTT "PTT"
#define KEY_VFO_DOWN "VFO_DOWN"
With the keys defined, parser code for these options is added to radio_conf_read() in the same file:
diff --git a/src/radio-conf.c b/src/radio-conf.c
index 46c70c5..2dc3954 100644
--- a/src/radio-conf.c
+++ b/src/radio-conf.c
@@ -175,6 +177,48 @@ gboolean radio_conf_read(radio_conf_t * conf)
conf->loup = 0.0;
}
+ /* KEY_DEF_UP_FREQ is optional */
+ if (g_key_file_has_key(cfg, GROUP, KEY_DEF_UP_FREQ, NULL))
+ {
+ conf->defUpFreq =
+ g_key_file_get_double(cfg, GROUP, KEY_DEF_UP_FREQ, &error);
+ if (error != NULL)
+ {
+ sat_log_log(SAT_LOG_LEVEL_ERROR,
+ _("%s: Error reading radio conf from %s (%s)."),
+ __func__, conf->name, error->message);
+ g_clear_error(&error);
+ g_key_file_free(cfg);
+ return FALSE;
+ }
+ }
+ else
+ {
+ conf->defUpFreq = 145890000.0;
+ }
The parser for KEY_DEF_DN_FREQ is similar.
Finally, we must also remember to save these configuration settings in radio_conf_save:
@@ -272,6 +316,8 @@ void radio_conf_save(radio_conf_t * conf)
g_key_file_set_integer(cfg, GROUP, KEY_PORT, conf->port);
g_key_file_set_double(cfg, GROUP, KEY_LO, conf->lo);
g_key_file_set_double(cfg, GROUP, KEY_LOUP, conf->loup);
+ g_key_file_set_double(cfg, GROUP, KEY_DEF_UP_FREQ, conf->defUpFreq);
+ g_key_file_set_double(cfg, GROUP, KEY_DEF_DN_FREQ, conf->defDnFreq);
g_key_file_set_integer(cfg, GROUP, KEY_TYPE, conf->type);
g_key_file_set_integer(cfg, GROUP, KEY_PTT, conf->ptt);
Modifying the rig editor dialog window
Adding these options to the rig editor UI involves modifying the following methods in sat-pref-rig-editor.c:
clear_widgets(), which initializes widgets to their default values,update_widgets(), which initializes the widgets with values read from a configuration file,create_editor_widgets(), which populates the UI controls, andapply_changes(), which saves changes to aradio_conf_tstruct.
Common
First, we must add two global pointers to sat-pref-rig-editor.c for the new widgets that we will be adding:
diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c
index 0f278fc..27d726a 100644
--- a/src/sat-pref-rig-editor.c
+++ b/src/sat-pref-rig-editor.c
@@ -41,6 +41,8 @@ static GtkWidget *ptt; /* PTT */
static GtkWidget *vfo; /* VFO Up/Down selector */
static GtkWidget *lo; /* local oscillator of downconverter */
static GtkWidget *loup; /* local oscillator of upconverter */
+static GtkWidget *defDnFreq; /* Default downlink frequency */
+static GtkWidget *defUpFreq; /* Default uplink frequency */
static GtkWidget *sigaos; /* AOS signalling */
static GtkWidget *siglos; /* LOS signalling */
create_editor_widgets()
Controls are added for adjusting downlink frequency to create_editor_widgets():
diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c
index 0f278fc..27d726a 100644
--- a/src/sat-pref-rig-editor.c
+++ b/src/sat-pref-rig-editor.c
@@ -421,18 +431,50 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf)
g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
gtk_grid_attach(GTK_GRID(table), label, 3, 7, 1, 1);
+ /* Default downlink frequency */
+ label = gtk_label_new(_("Default downlink"));
+ g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL);
+ gtk_grid_attach(GTK_GRID(table), label, 0, 8, 1, 1);
+
+ defDnFreq = gtk_spin_button_new_with_range(0, 10000, 0.001);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(defDnFreq), 0);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(defDnFreq), 3);
+ gtk_widget_set_tooltip_text(defDnFreq,
+ _("Enter the default downlink frequency"));
+ gtk_grid_attach(GTK_GRID(table), defDnFreq, 1, 8, 2, 1);
+
+ label = gtk_label_new(_("MHz"));
+ g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
+ gtk_grid_attach(GTK_GRID(table), label, 3, 8, 1, 1);
The code which adds uplink frequency adjustment is similar.
As two new rows are inserted in the widget table above AOS/LOS signaling, these controls must be shifted down by one:
/* AOS / LOS signalling */
label = gtk_label_new(_("Signalling"));
g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL);
- gtk_grid_attach(GTK_GRID(table), label, 0, 8, 1, 1);
+ gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1);
sigaos = gtk_check_button_new_with_label(_("AOS"));
- gtk_grid_attach(GTK_GRID(table), sigaos, 1, 8, 1, 1);
+ gtk_grid_attach(GTK_GRID(table), sigaos, 1, 10, 1, 1);
gtk_widget_set_tooltip_text(sigaos,
_("Enable AOS signalling for this radio."));
siglos = gtk_check_button_new_with_label(_("LOS"));
- gtk_grid_attach(GTK_GRID(table), siglos, 2, 8, 1, 1);
+ gtk_grid_attach(GTK_GRID(table), siglos, 2, 10, 1, 1);
gtk_widget_set_tooltip_text(siglos,
_("Enable LOS signalling for this radio."));
clear_editor_widgets()
Changes to this method are very simple:
@@ -52,6 +54,8 @@ static void clear_widgets()
gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), 4532); /* hamlib default? */
gtk_spin_button_set_value(GTK_SPIN_BUTTON(lo), 0);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(loup), 0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(defDnFreq), 145.89);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(defUpFreq), 145.89);
gtk_combo_box_set_active(GTK_COMBO_BOX(type), RIG_TYPE_RX);
gtk_combo_box_set_active(GTK_COMBO_BOX(ptt), PTT_TYPE_NONE);
gtk_combo_box_set_active(GTK_COMBO_BOX(vfo), 0);
update_widgets()
@@ -100,6 +104,12 @@ static void update_widgets(radio_conf_t * conf)
/* lo up in MHz */
gtk_spin_button_set_value(GTK_SPIN_BUTTON(loup), conf->loup / 1000000.0);
+ /* Default downlink frequency in MHz */
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(defDnFreq),
+ conf->defDnFreq * 1.0e-6);
+ /* Default uplink frequency in Mhz */
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(defUpFreq),
+ conf->defUpFreq * 1.0e-6);
/* AOS / LOS signalling */
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sigaos), conf->signal_aos);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(siglos), conf->signal_los);
apply_changes()
@@ -468,6 +512,14 @@ static gboolean apply_changes(radio_conf_t * conf)
/* lo up freq */
conf->loup = 1000000.0 * gtk_spin_button_get_value(GTK_SPIN_BUTTON(loup));
+ /* default downlink freq */
+ conf->defDnFreq = 1000000.0 *
+ gtk_spin_button_get_value(GTK_SPIN_BUTTON(defDnFreq));
+
+ /* default uplink freq */
+ conf->defUpFreq = 1000000.0 *
+ gtk_spin_button_get_value(GTK_SPIN_BUTTON(defUpFreq));
+
/* rig type */
conf->type = gtk_combo_box_get_active(GTK_COMBO_BOX(type));
Modifying the rig list
sat-pref-rig-data.h
First, we add enumeration values representing the two columns we will be adding to the rig list (default uplink and downlink frequency):
diff --git a/src/sat-pref-rig-data.h b/src/sat-pref-rig-data.h
index eea4e56..1330e07 100644
--- a/src/sat-pref-rig-data.h
+++ b/src/sat-pref-rig-data.h
@@ -38,6 +38,8 @@ typedef enum {
RIG_LIST_COL_VFODOWN, /*!< VFO down */
RIG_LIST_COL_LO, /*!< Local oscillator freq (downlink) */
RIG_LIST_COL_LOUP, /*!< Local oscillato freq (uplink) */
+ RIG_LIST_COL_DEF_UP_FREQ, /*!< Default uplink frequency */
+ RIG_LIST_COL_DEF_DN_FREQ, /*!< Default downlink frequency */
RIG_LIST_COL_SIGAOS, /*!< Signal AOS */
RIG_LIST_COL_SIGLOS, /*!< Signal LOS */
RIG_LIST_COL_NUM /*!< The number of fields in the list. */
sat-pref-rig.c
The data model
The list store (data model) must be modified to include the new columns:
@@ -53,13 +53,15 @@ static GtkTreeModel *create_and_fill_model()
/* create a new list store */
liststore = gtk_list_store_new(RIG_LIST_COL_NUM, G_TYPE_STRING, // name
G_TYPE_STRING, // host
G_TYPE_INT, // port
G_TYPE_INT, // type
G_TYPE_INT, // PTT
G_TYPE_INT, // VFO Up
G_TYPE_INT, // VFO Down
G_TYPE_DOUBLE, // LO DOWN
G_TYPE_DOUBLE, // LO UO
+ G_TYPE_DOUBLE, // Default down freq.
+ G_TYPE_DOUBLE, // Default up freq.
G_TYPE_BOOLEAN, // AOS signalling
G_TYPE_BOOLEAN // LOS signalling
);
Likewise every instance of gtk_list_store_set and gtk_tree_model_get must be modified to include these new columns:
gtk_list_store_set(liststore, &item,
RIG_LIST_COL_NAME, conf.name,
RIG_LIST_COL_HOST, conf.host,
RIG_LIST_COL_PORT, conf.port,
RIG_LIST_COL_TYPE, conf.type,
RIG_LIST_COL_PTT, conf.ptt,
RIG_LIST_COL_VFOUP, conf.vfoUp,
RIG_LIST_COL_VFODOWN, conf.vfoDown,
RIG_LIST_COL_LO, conf.lo,
RIG_LIST_COL_LOUP, conf.loup,
+ RIG_LIST_COL_DEF_UP_FREQ, conf.defUpFreq,
+ RIG_LIST_COL_DEF_DN_FREQ, conf.defDnFreq,
RIG_LIST_COL_SIGAOS, conf.signal_aos,
RIG_LIST_COL_SIGLOS, conf.signal_los,
-1);
}
The edit, add, and ok button callbacks must be changed:
@@ -393,6 +397,8 @@ static void edit_cb(GtkWidget * button, gpointer data)
.vfoDown = 0,
.lo = 0.0,
.loup = 0.0,
+ .defUpFreq = 0.0,
+ .defDnFreq = 0.0,
.signal_aos = FALSE,
.signal_los = FALSE
};
@@ -688,6 +719,8 @@ static void add_cb(GtkWidget * button, gpointer data)
.vfoDown = 0,
.lo = 0.0,
.loup = 0.0,
+ .defDnFreq = 14589000.0,
+ .defUpFreq = 14589000.0,
.signal_aos = FALSE,
.signal_los = FALSE,
};
@@ -812,6 +847,8 @@ void sat_pref_rig_ok()
.vfoDown = 0,
.lo = 0.0,
.loup = 0.0,
+ .defDnFreq = 14589000.0,
+ .defUpFreq = 14589000.0,
.signal_aos = FALSE,
.signal_los = FALSE
};
Adding UI controls
@@ -587,6 +597,27 @@ static void create_rig_list()
(RIG_LIST_COL_LOUP), NULL);
gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1);
+ /* Default downlink frequency */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Downlink"), renderer,
+ "text", RIG_LIST_COL_DEF_DN_FREQ,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer,
+ render_lo,
+ GUINT_TO_POINTER(RIG_LIST_COL_DEF_DN_FREQ),
+ NULL);
+ gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1);
+
+ /* Default uplink frequency */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Uplink"), renderer,
+ "text",
+ RIG_LIST_COL_DEF_UP_FREQ, NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, render_lo,
+ GUINT_TO_POINTER
+ (RIG_LIST_COL_DEF_UP_FREQ), NULL);
+ gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1);
+
/* AOS signalling */
renderer = gtk_cell_renderer_text_new();
column =
Lastly, the number of digits printed after the decimal point in the rig list columns is increased from 0 to 3:
@@ -293,7 +297,7 @@ static void render_lo(GtkTreeViewColumn * col,
/* convert to MHz */
number /= 1000000.0;
- buff = g_strdup_printf("%.0f MHz", number);
+ buff = g_strdup_printf("%.3f MHz", number);
g_object_set(renderer, "text", buff, NULL);
g_free(buff);
}
Pull request
A pull request implementing these changes has been opened: see #274.