From 01e2d1e5e84d341b50ae51913a9d8a4d64eb7900 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Thu, 13 Feb 2020 10:42:53 +0000 Subject: [PATCH 1/4] Add support for sending a user-defined rigctrld command on AOS or LOS. Add support for sending user-defined rigctrl commands when transponder is set. (Part of Issue #182) --- src/gtk-rig-ctrl.c | 95 +++++++++++++++++++++++++++++++++------ src/radio-conf.c | 11 +++++ src/radio-conf.h | 3 ++ src/sat-pref-rig-data.h | 2 + src/sat-pref-rig-editor.c | 61 +++++++++++++++++++++++++ src/sat-pref-rig.c | 55 ++++++++++++++++++++--- src/trsp-conf.c | 13 +++++- src/trsp-conf.h | 1 + 8 files changed, 221 insertions(+), 20 deletions(-) diff --git a/src/gtk-rig-ctrl.c b/src/gtk-rig-ctrl.c index 89247c34..75580569 100644 --- a/src/gtk-rig-ctrl.c +++ b/src/gtk-rig-ctrl.c @@ -84,6 +84,9 @@ static gboolean unset_toggle(GtkRigCtrl * ctrl, gint sock); static gboolean get_freq_toggle(GtkRigCtrl * ctrl, gint sock, gdouble * freq); static gboolean get_ptt(GtkRigCtrl * ctrl, gint sock); static gboolean set_ptt(GtkRigCtrl * ctrl, gint sock, gboolean ptt); +static gboolean send_rigctld_commands(GtkRigCtrl * ctrl, gint sock, + gchar * buff, gchar * buffout, + gint sizeout); /* add thread for hamlib communication */ gpointer rigctl_run(gpointer data); @@ -707,6 +710,17 @@ static void trsp_tune_cb(GtkButton * button, gpointer data) if (ctrl->trsp == NULL) return; + /* Send commands for this transponder (usually to set demodulation mode) */ + if (ctrl->trsp->command) + { + gchar retbuf[256]; + + if (!send_rigctld_commands(ctrl, ctrl->sock, ctrl->trsp->command, + retbuf, sizeof(retbuf))) + sat_log_log(SAT_LOG_LEVEL_ERROR, + "Error sending transponder command."); + } + /* tune downlink */ if ((ctrl->trsp->downlow > 0) && (ctrl->trsp->downhigh > 0)) { @@ -1447,6 +1461,55 @@ static gboolean send_rigctld_command(GtkRigCtrl * ctrl, gint sock, return (retval); } +/* Send a series of ; separated rigctld commands. + Return value will only be TRUE if all commands return TRUE. + We stop sending commands on error. + buffout will be valid for the last command only. + */ +static gboolean send_rigctld_commands(GtkRigCtrl * ctrl, gint sock, + gchar * buff, gchar * buffout, + gint sizeout) +{ + gboolean retval = TRUE; + gchar *cmds; + gchar *cmd; + gchar *end; + gint length; + + cmd = g_malloc(strlen(buff) + 2); + cmds = buff; + + while ((cmds[0] != '\0') && retval) + { + /* Calculate length up to ; or '\0' */ + end = strstr(cmds, ";"); + if (end == NULL) + length = strlen(cmds); + else + length = end - cmds; + if (length > 0) + { + /* Copy command and append newline and '\0' */ + strncpy(cmd, cmds, length); + cmd[length] = '\n'; + cmd[length+1] = '\0'; + /* Send the command. */ + retval = send_rigctld_command(ctrl, sock, cmd, buffout, sizeout); + /* Move past the command to the ; or '\0' */ + cmds += length; + } + else + { + /* Skip ; */ + cmds++; + } + } + + g_free(cmd); + + return (retval); +} + static inline gboolean check_set_response(gchar * buffback, gboolean retcode, const gchar * function) { @@ -2303,39 +2366,45 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) gboolean retcode = TRUE; gchar retbuf[10]; - if (ctrl->engaged && ctrl->tracking) + /* Don't check tracking, as we want AOS/LOS signalling, even if not + adjusting for doppler, which we may not want to do for some demodulators */ + if (ctrl->engaged) { if (ctrl->prev_ele < 0.0 && ctrl->target->el >= 0.0) { /* AOS has occurred */ - if (ctrl->conf->signal_aos) + if (ctrl->conf->signal_aos && ctrl->conf->aos_command) { - retcode &= send_rigctld_command(ctrl, ctrl->sock, "AOS\n", - retbuf, 10); + retcode &= send_rigctld_commands(ctrl, ctrl->sock, + ctrl->conf->aos_command, + retbuf, sizeof(retbuf)); } if (ctrl->conf2 != NULL) { - if (ctrl->conf2->signal_aos) + if (ctrl->conf2->signal_aos && ctrl->conf2->aos_command) { - retcode &= send_rigctld_command(ctrl, ctrl->sock2, "AOS\n", - retbuf, 10); + retcode &= send_rigctld_commands(ctrl, ctrl->sock2, + ctrl->conf2->aos_command, + retbuf, sizeof(retbuf)); } } } else if (ctrl->prev_ele >= 0.0 && ctrl->target->el < 0.0) { /* LOS has occurred */ - if (ctrl->conf->signal_los) + if (ctrl->conf->signal_los && ctrl->conf->los_command) { - retcode &= send_rigctld_command(ctrl, ctrl->sock, "LOS\n", - retbuf, 10); + retcode &= send_rigctld_commands(ctrl, ctrl->sock, + ctrl->conf->los_command, + retbuf, sizeof(retbuf)); } if (ctrl->conf2 != NULL) { - if (ctrl->conf2->signal_los) + if (ctrl->conf2->signal_los && ctrl->conf2->los_command) { - retcode &= send_rigctld_command(ctrl, ctrl->sock2, "LOS\n", - retbuf, 10); + retcode &= send_rigctld_commands(ctrl, ctrl->sock2, + ctrl->conf2->los_command, + retbuf, sizeof(retbuf)); } } } diff --git a/src/radio-conf.c b/src/radio-conf.c index 46c70c5d..467a809b 100644 --- a/src/radio-conf.c +++ b/src/radio-conf.c @@ -45,6 +45,8 @@ #define KEY_VFO_UP "VFO_UP" #define KEY_SIG_AOS "SIGNAL_AOS" #define KEY_SIG_LOS "SIGNAL_LOS" +#define KEY_AOS_COMMAND "AOS_COMMAND" +#define KEY_LOS_COMMAND "LOS_COMMAND" #define DEFAULT_CYCLE_MS 1000 @@ -237,6 +239,10 @@ gboolean radio_conf_read(radio_conf_t * conf) conf->signal_aos = g_key_file_get_boolean(cfg, GROUP, KEY_SIG_AOS, NULL); conf->signal_los = g_key_file_get_boolean(cfg, GROUP, KEY_SIG_LOS, NULL); + /* AOS and LOS commands */ + conf->aos_command = g_key_file_get_string(cfg, GROUP, KEY_AOS_COMMAND, NULL); + conf->los_command = g_key_file_get_string(cfg, GROUP, KEY_LOS_COMMAND, NULL); + g_key_file_free(cfg); sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Read radio configuration %s"), __func__, conf->name); @@ -289,6 +295,11 @@ void radio_conf_save(radio_conf_t * conf) g_key_file_set_boolean(cfg, GROUP, KEY_SIG_AOS, conf->signal_aos); g_key_file_set_boolean(cfg, GROUP, KEY_SIG_LOS, conf->signal_los); + if (conf->aos_command) + g_key_file_set_string(cfg, GROUP, KEY_AOS_COMMAND, conf->aos_command); + if (conf->los_command) + g_key_file_set_string(cfg, GROUP, KEY_LOS_COMMAND, conf->los_command); + confdir = get_hwconf_dir(); fname = g_strconcat(confdir, G_DIR_SEPARATOR_S, conf->name, ".rig", NULL); g_free(confdir); diff --git a/src/radio-conf.h b/src/radio-conf.h index 8dd9982a..784e18df 100644 --- a/src/radio-conf.h +++ b/src/radio-conf.h @@ -72,6 +72,9 @@ typedef struct { gboolean signal_aos; /*!< Send AOS notification to RIG */ gboolean signal_los; /*!< Send LOS notification to RIG */ + + gchar *aos_command; /*!< Commands to send to radio on AOS */ + gchar *los_command; /*!< Commands to send to radio on LOS */ } radio_conf_t; diff --git a/src/sat-pref-rig-data.h b/src/sat-pref-rig-data.h index eea4e561..3777d2f4 100644 --- a/src/sat-pref-rig-data.h +++ b/src/sat-pref-rig-data.h @@ -40,6 +40,8 @@ typedef enum { RIG_LIST_COL_LOUP, /*!< Local oscillato freq (uplink) */ RIG_LIST_COL_SIGAOS, /*!< Signal AOS */ RIG_LIST_COL_SIGLOS, /*!< Signal LOS */ + RIG_LIST_COL_AOS_COMMAND, /*!< AOS command */ + RIG_LIST_COL_LOS_COMMAND, /*!< LOS command */ RIG_LIST_COL_NUM /*!< The number of fields in the list. */ } rig_list_col_t; diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c index c5c8004e..6ec52f20 100644 --- a/src/sat-pref-rig-editor.c +++ b/src/sat-pref-rig-editor.c @@ -43,6 +43,8 @@ static GtkWidget *lo; /* local oscillator of downconverter */ static GtkWidget *loup; /* local oscillator of upconverter */ static GtkWidget *sigaos; /* AOS signalling */ static GtkWidget *siglos; /* LOS signalling */ +static GtkWidget *aos_command; /* AOS command */ +static GtkWidget *los_command; /* LOS command */ static void clear_widgets() @@ -58,6 +60,8 @@ static void clear_widgets() gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ptt), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sigaos), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(siglos), FALSE); + gtk_entry_set_text(GTK_ENTRY(aos_command), ""); + gtk_entry_set_text(GTK_ENTRY(los_command), ""); } static void update_widgets(radio_conf_t * conf) @@ -103,6 +107,14 @@ static void update_widgets(radio_conf_t * conf) /* 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); + + /* AOS / LOS commands */ + if (conf->aos_command) + gtk_entry_set_text(GTK_ENTRY(aos_command), conf->aos_command); + gtk_widget_set_sensitive(aos_command, conf->signal_aos); + if (conf->los_command) + gtk_entry_set_text(GTK_ENTRY(los_command), conf->los_command); + gtk_widget_set_sensitive(los_command, conf->signal_los); } /* @@ -225,6 +237,18 @@ static void type_changed(GtkWidget * widget, gpointer data) } } +/* Manage signal AOS change. */ +static void aos_changed(GtkWidget * widget, gpointer data) +{ + gtk_widget_set_sensitive(aos_command, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos))); +} + +/* Manage signal LOS change. */ +static void los_changed(GtkWidget * widget, gpointer data) +{ + gtk_widget_set_sensitive(los_command, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos))); +} + static GtkWidget *create_editor_widgets(radio_conf_t * conf) { GtkWidget *table; @@ -428,14 +452,39 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) sigaos = gtk_check_button_new_with_label(_("AOS")); gtk_grid_attach(GTK_GRID(table), sigaos, 1, 8, 1, 1); + g_signal_connect(sigaos, "toggled", G_CALLBACK(aos_changed), NULL); 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); + g_signal_connect(siglos, "toggled", G_CALLBACK(los_changed), NULL); gtk_widget_set_tooltip_text(siglos, _("Enable LOS signalling for this radio.")); + /* AOS command */ + label = gtk_label_new(_("AOS Command")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 9, 1, 1); + + aos_command = gtk_entry_new(); + gtk_widget_set_tooltip_text(aos_command, + _("Enter commands to send to the radio on AOS, " + "e.g. set_powerstat 1")); + gtk_grid_attach(GTK_GRID(table), aos_command, 1, 9, 3, 1); + + /* LOS command */ + label = gtk_label_new(_("LOS Command")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1); + + los_command = gtk_entry_new(); + gtk_widget_set_tooltip_text(los_command, + _("Enter commands to send to the radio on LOS, " + "e.g. set_powerstat 0")); + gtk_grid_attach(GTK_GRID(table), los_command, 1, 10, 3, 1); + + if (conf->name != NULL) update_widgets(conf); @@ -511,6 +560,18 @@ static gboolean apply_changes(radio_conf_t * conf) conf->signal_aos = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos)); conf->signal_los = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos)); + /* AOS command */ + if (conf->aos_command) + g_free(conf->aos_command); + + conf->aos_command = g_strdup(gtk_entry_get_text(GTK_ENTRY(aos_command))); + + /* LOS command */ + if (conf->los_command) + g_free(conf->los_command); + + conf->los_command = g_strdup(gtk_entry_get_text(GTK_ENTRY(los_command))); + return TRUE; } diff --git a/src/sat-pref-rig.c b/src/sat-pref-rig.c index 9c85bd46..f9016fcc 100644 --- a/src/sat-pref-rig.c +++ b/src/sat-pref-rig.c @@ -61,7 +61,9 @@ static GtkTreeModel *create_and_fill_model() G_TYPE_DOUBLE, // LO DOWN G_TYPE_DOUBLE, // LO UO G_TYPE_BOOLEAN, // AOS signalling - G_TYPE_BOOLEAN // LOS signalling + G_TYPE_BOOLEAN, // LOS signalling + G_TYPE_STRING, // AOS command + G_TYPE_STRING // LOS command ); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), @@ -98,6 +100,8 @@ static GtkTreeModel *create_and_fill_model() RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_COMMAND, conf.aos_command, + RIG_LIST_COL_LOS_COMMAND, conf.los_command, -1); sat_log_log(SAT_LOG_LEVEL_DEBUG, @@ -426,7 +430,10 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LO, &conf.lo, RIG_LIST_COL_LOUP, &conf.loup, RIG_LIST_COL_SIGAOS, &conf.signal_aos, - RIG_LIST_COL_SIGLOS, &conf.signal_los, -1); + RIG_LIST_COL_SIGLOS, &conf.signal_los, + RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, + RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + -1); } else { @@ -462,7 +469,10 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LO, conf.lo, RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, - RIG_LIST_COL_SIGLOS, conf.signal_los, -1); + RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_COMMAND, conf.aos_command, + RIG_LIST_COL_LOS_COMMAND, conf.los_command, + -1); } /* clean up memory */ @@ -609,8 +619,31 @@ static void create_rig_list() (RIG_LIST_COL_SIGLOS), NULL); gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* AOS command */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("AOS Command"), + renderer, + "text", + RIG_LIST_COL_AOS_COMMAND, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* LOS command */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("LOS Command"), + renderer, + "text", + RIG_LIST_COL_LOS_COMMAND, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + + + g_signal_connect(riglist, "row-activated", G_CALLBACK(row_activated_cb), riglist); + } /** @@ -690,6 +723,8 @@ static void add_cb(GtkWidget * button, gpointer data) .loup = 0.0, .signal_aos = FALSE, .signal_los = FALSE, + .aos_command = NULL, + .los_command = NULL }; /* run rig conf editor */ @@ -712,7 +747,10 @@ static void add_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LO, conf.lo, RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, - RIG_LIST_COL_SIGLOS, conf.signal_los, -1); + RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_COMMAND, conf.aos_command, + RIG_LIST_COL_LOS_COMMAND, conf.los_command, + -1); g_free(conf.name); @@ -813,7 +851,9 @@ void sat_pref_rig_ok() .lo = 0.0, .loup = 0.0, .signal_aos = FALSE, - .signal_los = FALSE + .signal_los = FALSE, + .aos_command = NULL, + .los_command = NULL }; /* delete all .rig files */ @@ -859,7 +899,10 @@ void sat_pref_rig_ok() RIG_LIST_COL_LO, &conf.lo, RIG_LIST_COL_LOUP, &conf.loup, RIG_LIST_COL_SIGAOS, &conf.signal_aos, - RIG_LIST_COL_SIGLOS, &conf.signal_los, -1); + RIG_LIST_COL_SIGLOS, &conf.signal_los, + RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, + RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + -1); radio_conf_save(&conf); /* free conf buffer */ diff --git a/src/trsp-conf.c b/src/trsp-conf.c index b1ca3f1f..7900797d 100644 --- a/src/trsp-conf.c +++ b/src/trsp-conf.c @@ -40,6 +40,7 @@ #define KEY_INVERT "INVERT" #define KEY_MODE "MODE" #define KEY_BAUD "BAUD" +#define KEY_COMMAND "COMMAND" static void check_trsp_freq(trsp_t * trsp) { @@ -180,7 +181,6 @@ GSList *read_transponders(guint catnum) name, groups[i]); g_clear_error(&error); } - trsp->baud = g_key_file_get_double(cfg, groups[i], KEY_BAUD, &error); if (error != NULL) { @@ -189,6 +189,15 @@ GSList *read_transponders(guint catnum) g_clear_error(&error); } + trsp->command = g_key_file_get_string(cfg, groups[i], KEY_COMMAND, &error); + if (error != NULL) + { + sat_log_log(SAT_LOG_LEVEL_INFO, INFO_MSG, __func__, KEY_COMMAND, + name, groups[i]); + g_clear_error(&error); + } + + /* add transponder to list */ trsplist = g_slist_append(trsplist, trsp); } @@ -254,6 +263,8 @@ void write_transponders(guint catnum, GSList * trsp_list) g_key_file_set_boolean(trsp_data, trsp->name, KEY_INVERT, TRUE); if (trsp->mode) g_key_file_set_string(trsp_data, trsp->name, KEY_MODE, trsp->name); + if (trsp->command) + g_key_file_set_string(trsp_data, trsp->name, KEY_COMMAND, trsp->command); } if (gpredict_save_key_file(trsp_data, trsp_file)) diff --git a/src/trsp-conf.h b/src/trsp-conf.h index 035f8c2a..9342178b 100644 --- a/src/trsp-conf.h +++ b/src/trsp-conf.h @@ -39,6 +39,7 @@ typedef struct { gdouble baud; /*!< Baud rate > */ gboolean invert; /*!< Flag indicating whether transponder is inverting. */ gchar *mode; /*!< Mode descriptor. */ + gchar *command; /*!< Command(s) to send to radio when selecting this transponder. */ } trsp_t; /* The actual data would then be a singly linked list with pointers to transponder_t structures */ From d391ce92ff0e3120a5273e3c70b047e4f7b33d45 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 18 Feb 2020 14:12:17 +0000 Subject: [PATCH 2/4] Support playing audio files on AOS and LOS. Issue 187. Allow elevation for AOS and LOS to be set to something other than 0 degrees. Issue 167. Signal LOS when autotrack is enabled. Issue 193. Update Windows makefile to work with latest version of msys2. Issue 110. --- configure.ac | 16 ++++ src/Makefile.am | 1 + src/gtk-audio.c | 81 +++++++++++++++++++ src/gtk-audio.h | 21 +++++ src/gtk-rig-ctrl.c | 36 +++++++-- src/gtk-sat-module.c | 44 +++++++++- src/main.c | 6 ++ src/predict-tools.c | 28 +++++++ src/predict-tools.h | 1 + src/radio-conf.c | 24 +++++- src/radio-conf.h | 6 ++ src/sat-pref-rig-data.h | 4 + src/sat-pref-rig-editor.c | 165 ++++++++++++++++++++++++++++++++++++-- src/sat-pref-rig.c | 109 ++++++++++++++++++++++++- win32/Makefile | 41 ++++++---- win32/build-config.h | 2 + win32/config.mk | 30 +++---- win32/loaders.cache | 37 +++++---- 18 files changed, 578 insertions(+), 74 deletions(-) create mode 100644 src/gtk-audio.c create mode 100644 src/gtk-audio.h diff --git a/configure.ac b/configure.ac index 058a57ef..e6064fba 100644 --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,16 @@ else AC_MSG_ERROR(Gpredict requires libgoocanvas-2.0-dev) fi +# check for gstreamer (optional) +if pkg-config --atleast-version=1.0 gstreamer-1.0; then + CFLAGS="$CFLAGS `pkg-config --cflags gstreamer-1.0`" + LIBS="$LIBS `pkg-config --libs gstreamer-1.0`" + havelibgstreamer=true; + AC_DEFINE(HAS_LIBGSTREAMER, 1, [Define if libgstreamer is available]) +else + havelibgstreamer=false; +fi + # check for libgps (optional) if pkg-config --atleast-version=2.90 libgps; then CFLAGS="$CFLAGS `pkg-config --cflags libgps`" @@ -94,6 +104,11 @@ GTHR_V=`pkg-config --modversion gthread-2.0` GDK_V=`pkg-config --modversion gdk-3.0` GTK_V=`pkg-config --modversion gtk+-3.0` GOOC_V=`pkg-config --modversion goocanvas-2.0` +if test "$havelibgstreamer" = true ; then + GST_V=`pkg-config --modversion gstreamer-1.0` +else + GST_V='Not found' +fi CURL_V=`pkg-config --modversion libcurl` if test "$havelibgps" = true ; then GPS_V=`pkg-config --modversion libgps` @@ -132,6 +147,7 @@ echo Gthread version.... : $GTHR_V echo Gdk version........ : $GDK_V echo Gtk+ version....... : $GTK_V echo GooCanvas version.. : $GOOC_V +echo gstreamer version.. : $GST_V echo Libcurl version.... : $CURL_V if test "$havelibgps" = true ; then echo Libgps version..... : $GPS_V diff --git a/src/Makefile.am b/src/Makefile.am index 020f4758..8586a40b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ gpredict_SOURCES = \ first-time.c first-time.h \ gpredict-help.c gpredict-help.h \ gpredict-utils.c gpredict-utils.h \ + gtk-audio.c gtk-audio.h \ gtk-azel-plot.c gtk-azel-plot.h \ gtk-event-list.c gtk-event-list.h \ gtk-event-list-popup.c gtk-event-list-popup.h \ diff --git a/src/gtk-audio.c b/src/gtk-audio.c new file mode 100644 index 00000000..dd65aa05 --- /dev/null +++ b/src/gtk-audio.c @@ -0,0 +1,81 @@ +/* Functions to play audio files (.wav etc). + * + * This uses the gstreamer library & requires the playbin and wav plugins. + * On Windows/msys2, these can be installed with: + * pacman -S mingw-w64-i686-gstreamer mingw-w64-i686-gst-plugins-base mingw-w64-i686-gst-plugins-good + * On Centos 8: + * yum install gstreamer1 streamer1-plugins-good gstreamer1-devel + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#ifdef HAS_LIBGSTREAMER +#include +#endif + +#include "gtk-audio.h" +#include "sat-log.h" + +/* Convert path to absolute path */ +gchar * +absolute_path (const gchar *filename) +{ + gchar *abs_path; + + if (!g_path_is_absolute (filename)) + { + gchar *cwd = NULL; + + cwd = g_get_current_dir(); + abs_path = g_build_filename(cwd, filename, NULL); + g_free(cwd); + } + else + abs_path = g_strdup(filename); + + return abs_path; +} + +#ifdef HAS_LIBGSTREAMER +void audio_play_uri(gchar *uri) +{ + GstElement *pipeline; + + /* playbin requires an absolute URI. E.g. file:///absolute/path. + We try to see if the input uri is actually a file path. If so, we make it + into a uri. + */ + if (g_file_test(uri, G_FILE_TEST_EXISTS)) + { + gchar *filename; + GError *error = NULL; + + filename = absolute_path(uri); + uri = g_filename_to_uri(filename, NULL, &error); + g_free(filename); + } + + /* Play audio using playbin plugin. */ + pipeline = gst_element_factory_make("playbin", NULL); + if (!pipeline) { + /* This will fail if the playbin plugin isn't found. */ + sat_log_log(SAT_LOG_LEVEL_ERROR, + _("%s: Failed to create playbin pipeline. Are gst plugins installed?"), + __func__); + return; + } + g_object_set(pipeline, "uri", uri, NULL); + gst_element_set_state(pipeline, GST_STATE_PLAYING); +} +#else +void audio_play_uri(gchar *uri) +{ + sat_log_log(SAT_LOG_LEVEL_WARN, + _("%s: Request to play audio \"%s\", but gpredict was compiled without libgstreamer"), + __func__, uri); +} +#endif diff --git a/src/gtk-audio.h b/src/gtk-audio.h new file mode 100644 index 00000000..02686f1d --- /dev/null +++ b/src/gtk-audio.h @@ -0,0 +1,21 @@ +#ifndef __GTK_AUDIO_H +#define __GTK_AUDIO_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/**< Play audio file. E.g. .wav + uri can be a relative pathname or URI. + E.g. ../path/file.wav, file:///path/something.wav or http://server/file.wav + Supported file formats depend on which plugins are installed. + */ +void audio_play_uri(gchar *uri); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/gtk-rig-ctrl.c b/src/gtk-rig-ctrl.c index 75580569..ca9a51b6 100644 --- a/src/gtk-rig-ctrl.c +++ b/src/gtk-rig-ctrl.c @@ -60,6 +60,7 @@ #include "sat-log.h" #include "sat-cfg.h" #include "trsp-conf.h" +#include "gtk-audio.h" #define AZEL_FMTSTR "%7.2f\302\260" @@ -215,7 +216,7 @@ static void update_count_down(GtkRigCtrl * ctrl, gdouble t) gchar *aoslos; /* select AOS or LOS time depending on target elevation */ - if (ctrl->target->el < 0.0) + if (ctrl->target->el < ctrl->conf->aos_el) { targettime = ctrl->target->aos; aoslos = g_strdup_printf(_("AOS in")); @@ -323,13 +324,15 @@ void gtk_rig_ctrl_update(GtkRigCtrl * ctrl, gdouble t) if (ctrl->target->aos > ctrl->pass->aos) { free_pass(ctrl->pass); - ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); + ctrl->pass = get_next_pass_el(ctrl->target, ctrl->qth, 3.0, + ctrl->conf->aos_el); } } else { /* we don't have any current pass; store the current one */ - ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); + ctrl->pass = get_next_pass_el(ctrl->target, ctrl->qth, 3.0, + ctrl->conf->aos_el); } } @@ -394,6 +397,12 @@ void gtk_rig_ctrl_select_sat(GtkRigCtrl * ctrl, gint catnum) sat_t *sat; int i, n; + /* This is called indirectly by update_autotrack, when a satellite goes + below the horizon. So we need to call check_aos_los here, to give it a + chance to signal LOS, otherwise it will be missed, as the current sat + will have changed on the next call. */ + check_aos_los(ctrl); + /* find index in satellite list */ n = g_slist_length(ctrl->sats); for (i = 0; i < n; i++) @@ -667,7 +676,8 @@ static void sat_selected_cb(GtkComboBox * satsel, gpointer data) /* update next pass */ if (ctrl->pass != NULL) free_pass(ctrl->pass); - ctrl->pass = get_next_pass(ctrl->target, ctrl->qth, 3.0); + ctrl->pass = get_next_pass_el(ctrl->target, ctrl->qth, 3.0, + ctrl->conf->aos_el); /* read transponders for new target */ load_trsp_list(ctrl); @@ -2370,9 +2380,12 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) adjusting for doppler, which we may not want to do for some demodulators */ if (ctrl->engaged) { - if (ctrl->prev_ele < 0.0 && ctrl->target->el >= 0.0) + if (ctrl->prev_ele < ctrl->conf->aos_el + && ctrl->target->el >= ctrl->conf->aos_el) { /* AOS has occurred */ + if (ctrl->conf->signal_aos && ctrl->conf->aos_wav) + audio_play_uri(ctrl->conf->aos_wav); if (ctrl->conf->signal_aos && ctrl->conf->aos_command) { retcode &= send_rigctld_commands(ctrl, ctrl->sock, @@ -2389,9 +2402,12 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) } } } - else if (ctrl->prev_ele >= 0.0 && ctrl->target->el < 0.0) + else if (ctrl->prev_ele >= ctrl->conf->los_el + && ctrl->target->el < ctrl->conf->los_el) { /* LOS has occurred */ + if (ctrl->conf->signal_los && ctrl->conf->los_wav) + audio_play_uri(ctrl->conf->los_wav); if (ctrl->conf->signal_los && ctrl->conf->los_command) { retcode &= send_rigctld_commands(ctrl, ctrl->sock, @@ -3010,8 +3026,12 @@ GtkWidget *gtk_rig_ctrl_new(GtkSatModule * module) if (rigctrl->target != NULL) { /* get next pass for target satellite */ - GTK_RIG_CTRL(widget)->pass = get_next_pass(rigctrl->target, - rigctrl->qth, 3.0); + GTK_RIG_CTRL(widget)->pass = get_next_pass_el(rigctrl->target, + rigctrl->qth, + 3.0, + rigctrl->conf + ? rigctrl->conf->aos_el + : 0.0); } /* create contents */ diff --git a/src/gtk-sat-module.c b/src/gtk-sat-module.c index faf7f75b..cd6fa8c9 100644 --- a/src/gtk-sat-module.c +++ b/src/gtk-sat-module.c @@ -80,12 +80,28 @@ static void update_autotrack(GtkSatModule * module) guint i, n; double next_aos; gint next_sat; + double aos_el; + double los_el; + gdouble maxdt; + + /* Get AOS/LOS elevation for rig */ + aos_el = 0.0; + los_el = 0.0; + if (module->rigctrl != NULL) + { + GtkRigCtrl *ctrl = GTK_RIG_CTRL(module->rigctrl); + if ((ctrl != NULL) && (ctrl->conf != NULL)) + { + aos_el = ctrl->conf->aos_el; + los_el = ctrl->conf->los_el; + } + } if (module->target > 0) sat = g_hash_table_lookup(module->satellites, &module->target); /* do nothing if current target is still above horizon */ - if (sat != NULL && sat->el > 0.0) + if (sat != NULL && sat->el > los_el) return; /* set target to satellite with next AOS */ @@ -98,13 +114,15 @@ static void update_autotrack(GtkSatModule * module) next_aos = module->tmgCdnum + 10.f; /* hope there is AOS within 10 days */ next_sat = module->target; + maxdt = (gdouble) sat_cfg_get_int(SAT_CFG_INT_PRED_LOOK_AHEAD); + i = 0; while (i++ < n) { sat = (sat_t *) iter->data; /* if sat is above horizon, select it and we are done */ - if (sat->el > 0.0) + if (sat->el > aos_el) { next_sat = sat->tle.catnr; break; @@ -113,8 +131,26 @@ static void update_autotrack(GtkSatModule * module) /* we have a candidate if AOS is in the future */ if (sat->aos > module->tmgCdnum && sat->aos < next_aos) { - next_aos = sat->aos; - next_sat = sat->tle.catnr; + /* sat->aos is at the horizon, not rigs desired elevation for AOS, + so we calculate when the next pass at the desired elevation is */ + if (aos_el != 0.0) + { + pass_t *pass; + + /* Should we avoid recalculating this? */ + pass = get_next_pass_el(sat, module->qth, maxdt, aos_el); + if ((pass != NULL) && (pass->aos < next_aos)) + { + next_aos = pass->aos; + next_sat = sat->tle.catnr; + free_pass (pass); + } + } + else + { + next_aos = sat->aos; + next_sat = sat->tle.catnr; + } } iter = iter->next; diff --git a/src/main.c b/src/main.c index 2f17776f..451bd6b3 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,9 @@ #include #include #include +#ifdef HAS_LIBGSTREAMER +#include +#endif #include #include #ifdef WIN32 @@ -101,6 +104,9 @@ int main(int argc, char *argv[]) textdomain(PACKAGE); #endif gtk_init(&argc, &argv); +#ifdef HAS_LIBGSTREAMER + gst_init(&argc, &argv); +#endif context = g_option_context_new(""); g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE); diff --git a/src/predict-tools.c b/src/predict-tools.c index 6064cb20..cb6c76b6 100644 --- a/src/predict-tools.c +++ b/src/predict-tools.c @@ -369,6 +369,33 @@ pass_t *get_next_pass(sat_t * sat, qth_t * qth, gdouble maxdt) return get_pass(sat, qth, now, maxdt); } +/** + * \brief Predict the next pass that has a minimum elevation. + * \param sat Pointer to the satellite data. + * \param qth Pointer to the observer data. + * \param maxdt The maximum number of days to look ahead. + * \param min_ele Minimum elevation. + * \return Pointer newly allocated pass_t structure that should be freed + * with free_pass when no longer needed, or NULL if no pass can be + * found. + * + * This function simply wraps the get_pass function using the current time + * as parameter. + * + * \note the data in sat will be corrupt (future) and must be refreshed + * by the caller, if the caller will need it later on (eg. if the caller + * is GtkSatList). + */ +pass_t *get_next_pass_el(sat_t * sat, qth_t * qth, gdouble maxdt, gdouble min_ele) +{ + gdouble now; + + /* get the current time and call the get_pass function */ + now = get_current_daynum(); + + return get_pass_engine(sat, qth, now, maxdt, min_ele); +} + /** * \brief Predict upcoming passes starting now * \param sat Pointer to the satellite data. @@ -441,6 +468,7 @@ pass_t *get_pass_no_min_el(sat_t * sat_in, qth_t * qth, gdouble start, * \param qth Pointer to the location data. * \param start Starting time. * \param maxdt The maximum number of days to look ahead (0 for no limit). + * \param min_el Minimum elevation for the pass. * \return Pointer to a newly allocated pass_t structure or NULL if * there was an error. * diff --git a/src/predict-tools.h b/src/predict-tools.h index 0e89347e..a8191838 100644 --- a/src/predict-tools.h +++ b/src/predict-tools.h @@ -92,6 +92,7 @@ gdouble find_prev_aos (sat_t *sat, qth_t *qth, gdouble start); /* next events */ pass_t *get_next_pass (sat_t *sat, qth_t *qth, gdouble maxdt); +pass_t *get_next_pass_el (sat_t *sat, qth_t *qth, gdouble maxdt, gdouble min_el); GSList *get_next_passes (sat_t *sat, qth_t *qth, gdouble maxdt, guint num); /* future events */ diff --git a/src/radio-conf.c b/src/radio-conf.c index 467a809b..eae7a0f8 100644 --- a/src/radio-conf.c +++ b/src/radio-conf.c @@ -45,8 +45,12 @@ #define KEY_VFO_UP "VFO_UP" #define KEY_SIG_AOS "SIGNAL_AOS" #define KEY_SIG_LOS "SIGNAL_LOS" -#define KEY_AOS_COMMAND "AOS_COMMAND" -#define KEY_LOS_COMMAND "LOS_COMMAND" +#define KEY_AOS_ELEVATION "AOS_ELEVATION" +#define KEY_LOS_ELEVATION "LOS_ELEVATION" +#define KEY_AOS_COMMAND "AOS_COMMAND" +#define KEY_LOS_COMMAND "LOS_COMMAND" +#define KEY_AOS_WAV "AOS_WAV" +#define KEY_LOS_WAV "LOS_WAV" #define DEFAULT_CYCLE_MS 1000 @@ -239,10 +243,18 @@ gboolean radio_conf_read(radio_conf_t * conf) conf->signal_aos = g_key_file_get_boolean(cfg, GROUP, KEY_SIG_AOS, NULL); conf->signal_los = g_key_file_get_boolean(cfg, GROUP, KEY_SIG_LOS, NULL); + /* AOS and LOS elevation */ + conf->aos_el = g_key_file_get_double(cfg, GROUP, KEY_AOS_ELEVATION, NULL); + conf->los_el = g_key_file_get_double(cfg, GROUP, KEY_LOS_ELEVATION, NULL); + /* AOS and LOS commands */ conf->aos_command = g_key_file_get_string(cfg, GROUP, KEY_AOS_COMMAND, NULL); conf->los_command = g_key_file_get_string(cfg, GROUP, KEY_LOS_COMMAND, NULL); + /* AOS and LOS .wav files */ + conf->aos_wav = g_key_file_get_string(cfg, GROUP, KEY_AOS_WAV, NULL); + conf->los_wav = g_key_file_get_string(cfg, GROUP, KEY_LOS_WAV, NULL); + g_key_file_free(cfg); sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Read radio configuration %s"), __func__, conf->name); @@ -295,11 +307,19 @@ void radio_conf_save(radio_conf_t * conf) g_key_file_set_boolean(cfg, GROUP, KEY_SIG_AOS, conf->signal_aos); g_key_file_set_boolean(cfg, GROUP, KEY_SIG_LOS, conf->signal_los); + g_key_file_set_double(cfg, GROUP, KEY_AOS_ELEVATION, conf->aos_el); + g_key_file_set_double(cfg, GROUP, KEY_LOS_ELEVATION, conf->los_el); + if (conf->aos_command) g_key_file_set_string(cfg, GROUP, KEY_AOS_COMMAND, conf->aos_command); if (conf->los_command) g_key_file_set_string(cfg, GROUP, KEY_LOS_COMMAND, conf->los_command); + if (conf->aos_wav) + g_key_file_set_string(cfg, GROUP, KEY_AOS_WAV, conf->aos_wav); + if (conf->los_wav) + g_key_file_set_string(cfg, GROUP, KEY_LOS_WAV, conf->los_wav); + confdir = get_hwconf_dir(); fname = g_strconcat(confdir, G_DIR_SEPARATOR_S, conf->name, ".rig", NULL); g_free(confdir); diff --git a/src/radio-conf.h b/src/radio-conf.h index 784e18df..b112943e 100644 --- a/src/radio-conf.h +++ b/src/radio-conf.h @@ -73,8 +73,14 @@ typedef struct { gboolean signal_aos; /*!< Send AOS notification to RIG */ gboolean signal_los; /*!< Send LOS notification to RIG */ + gdouble aos_el; /*!< Elevation for AOS to be signalled at */ + gdouble los_el; /*!< Elevation for LOS to be signalled at */ + gchar *aos_command; /*!< Commands to send to radio on AOS */ gchar *los_command; /*!< Commands to send to radio on LOS */ + + gchar *aos_wav; /*!< Audio file (E.g. .wav) to play on AOS */ + gchar *los_wav; /*!< Audio file (E.g. .wav) to play on LOS */ } radio_conf_t; diff --git a/src/sat-pref-rig-data.h b/src/sat-pref-rig-data.h index 3777d2f4..f4f117d0 100644 --- a/src/sat-pref-rig-data.h +++ b/src/sat-pref-rig-data.h @@ -40,8 +40,12 @@ typedef enum { RIG_LIST_COL_LOUP, /*!< Local oscillato freq (uplink) */ RIG_LIST_COL_SIGAOS, /*!< Signal AOS */ RIG_LIST_COL_SIGLOS, /*!< Signal LOS */ + RIG_LIST_COL_AOS_ELEVATION, /*!< AOS elevation */ + RIG_LIST_COL_LOS_ELEVATION, /*!< LOS elevation */ RIG_LIST_COL_AOS_COMMAND, /*!< AOS command */ RIG_LIST_COL_LOS_COMMAND, /*!< LOS command */ + RIG_LIST_COL_AOS_WAV, /*!< AOS audio file */ + RIG_LIST_COL_LOS_WAV, /*!< LOS audio file */ RIG_LIST_COL_NUM /*!< The number of fields in the list. */ } rig_list_col_t; diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c index 6ec52f20..c5a1380a 100644 --- a/src/sat-pref-rig-editor.c +++ b/src/sat-pref-rig-editor.c @@ -43,8 +43,14 @@ static GtkWidget *lo; /* local oscillator of downconverter */ static GtkWidget *loup; /* local oscillator of upconverter */ static GtkWidget *sigaos; /* AOS signalling */ static GtkWidget *siglos; /* LOS signalling */ +static GtkWidget *aos_el; /* AOS elevation */ +static GtkWidget *los_el; /* LOS elevation */ static GtkWidget *aos_command; /* AOS command */ static GtkWidget *los_command; /* LOS command */ +static GtkWidget *aos_wav; /* AOS audio file */ +static GtkWidget *los_wav; /* LOS audio file */ +static GtkWidget *aos_wav_but; /* AOS audio file select button */ +static GtkWidget *los_wav_but; /* LOS audio file select button */ static void clear_widgets() @@ -60,8 +66,12 @@ static void clear_widgets() gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ptt), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sigaos), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(siglos), FALSE); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(aos_el), 0.0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(los_el), 0.0); gtk_entry_set_text(GTK_ENTRY(aos_command), ""); gtk_entry_set_text(GTK_ENTRY(los_command), ""); + gtk_entry_set_text(GTK_ENTRY(aos_wav), ""); + gtk_entry_set_text(GTK_ENTRY(los_wav), ""); } static void update_widgets(radio_conf_t * conf) @@ -108,6 +118,12 @@ static void update_widgets(radio_conf_t * conf) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sigaos), conf->signal_aos); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(siglos), conf->signal_los); + /* AOS / LOS elevation */ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(aos_el), conf->aos_el); + gtk_widget_set_sensitive(aos_el, conf->signal_aos); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(los_el), conf->los_el); + gtk_widget_set_sensitive(los_el, conf->signal_los); + /* AOS / LOS commands */ if (conf->aos_command) gtk_entry_set_text(GTK_ENTRY(aos_command), conf->aos_command); @@ -115,6 +131,14 @@ static void update_widgets(radio_conf_t * conf) if (conf->los_command) gtk_entry_set_text(GTK_ENTRY(los_command), conf->los_command); gtk_widget_set_sensitive(los_command, conf->signal_los); + + /* AOS / LOS .wav files */ + if (conf->aos_wav) + gtk_entry_set_text(GTK_ENTRY(aos_wav), conf->aos_wav); + gtk_widget_set_sensitive(aos_wav, conf->signal_aos); + if (conf->los_wav) + gtk_entry_set_text(GTK_ENTRY(los_wav), conf->los_wav); + gtk_widget_set_sensitive(los_wav, conf->signal_los); } /* @@ -240,13 +264,63 @@ static void type_changed(GtkWidget * widget, gpointer data) /* Manage signal AOS change. */ static void aos_changed(GtkWidget * widget, gpointer data) { - gtk_widget_set_sensitive(aos_command, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos))); + gboolean active; + + active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos)); + gtk_widget_set_sensitive(aos_el, active); + gtk_widget_set_sensitive(aos_command, active); + gtk_widget_set_sensitive(aos_wav, active); } /* Manage signal LOS change. */ static void los_changed(GtkWidget * widget, gpointer data) { - gtk_widget_set_sensitive(los_command, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos))); + gboolean active; + + active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos)); + gtk_widget_set_sensitive(los_el, active); + gtk_widget_set_sensitive(los_command, active); + gtk_widget_set_sensitive(los_wav, active); +} + +/** + * Select a file + * @param button Pointer to the button that was pressed + * @param data Pointer to entry widget to set with selected file name + */ +static void select_file_cb(GtkWidget * button, gpointer data) +{ + GtkWidget *chooser; + const gchar *oldfile; + + /* create a new file chooser dialogue in "open file" mode */ + chooser = gtk_file_chooser_dialog_new(_("Select audio file"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE); + + /* select existing file, if valid. */ + oldfile = gtk_entry_get_text(GTK_ENTRY(data)); + if (g_file_test(oldfile, G_FILE_TEST_EXISTS)) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(chooser), oldfile); + + /* run the dialogue */ + if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) + { + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); + + /* Update entry with chosen file */ + gtk_entry_set_text(GTK_ENTRY(data), filename); + + g_free (filename); + } + + gtk_widget_destroy(chooser); } static GtkWidget *create_editor_widgets(radio_conf_t * conf) @@ -462,28 +536,91 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) gtk_widget_set_tooltip_text(siglos, _("Enable LOS signalling for this radio.")); + /* AOS elevation */ + label = gtk_label_new(_("AOS Elevation")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 9, 1, 1); + + aos_el = gtk_spin_button_new_with_range(-90.0, 90.0, 1.0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(aos_el), 0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(aos_el), 1); + gtk_widget_set_tooltip_text(aos_el, + _("Enter the elevation in degress at which AOS will be signalled.")); + gtk_grid_attach(GTK_GRID(table), aos_el, 1, 9, 2, 1); + + label = gtk_label_new(_("deg")); + g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 3, 9, 1, 1); + + /* LOS elevation */ + label = gtk_label_new(_("LOS Elevation")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1); + + los_el = gtk_spin_button_new_with_range(-90.0, 90.0, 1.0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(los_el), 0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(los_el), 1); + gtk_widget_set_tooltip_text(los_el, + _("Enter the elevation in degress at which LOS will be signalled.")); + gtk_grid_attach(GTK_GRID(table), los_el, 1, 10, 2, 1); + + label = gtk_label_new(_("deg")); + g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 3, 10, 1, 1); + /* AOS command */ label = gtk_label_new(_("AOS Command")); g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 9, 1, 1); + gtk_grid_attach(GTK_GRID(table), label, 0, 11, 1, 1); aos_command = gtk_entry_new(); gtk_widget_set_tooltip_text(aos_command, _("Enter commands to send to the radio on AOS, " "e.g. set_powerstat 1")); - gtk_grid_attach(GTK_GRID(table), aos_command, 1, 9, 3, 1); + gtk_grid_attach(GTK_GRID(table), aos_command, 1, 11, 3, 1); /* LOS command */ label = gtk_label_new(_("LOS Command")); g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1); + gtk_grid_attach(GTK_GRID(table), label, 0, 12, 1, 1); los_command = gtk_entry_new(); gtk_widget_set_tooltip_text(los_command, _("Enter commands to send to the radio on LOS, " "e.g. set_powerstat 0")); - gtk_grid_attach(GTK_GRID(table), los_command, 1, 10, 3, 1); + gtk_grid_attach(GTK_GRID(table), los_command, 1, 12, 3, 1); + + /* AOS audio file */ + label = gtk_label_new(_("AOS Audio File")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 13, 1, 1); + aos_wav = gtk_entry_new(); + gtk_widget_set_tooltip_text(aos_wav, + _("Audio (E.g. .wav) file to play on AOS")); + gtk_grid_attach(GTK_GRID(table), aos_wav, 1, 13, 2, 1); + + aos_wav_but = gtk_button_new_with_label(_("Select")); + gtk_widget_set_tooltip_text(aos_wav_but, _("Click to select a file")); + g_signal_connect(G_OBJECT(aos_wav_but), "clicked", + G_CALLBACK(select_file_cb), aos_wav); + gtk_grid_attach(GTK_GRID(table), aos_wav_but, 3, 13, 1, 1); + + /* LOS audio file */ + label = gtk_label_new(_("LOS Audio File")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 14, 1, 1); + + los_wav = gtk_entry_new(); + gtk_widget_set_tooltip_text(los_wav, + _("Audio (E.g. .wav) file to play on LOS")); + gtk_grid_attach(GTK_GRID(table), los_wav, 1, 14, 2, 1); + + los_wav_but = gtk_button_new_with_label(_("Select")); + gtk_widget_set_tooltip_text(los_wav_but, _("Click to select a file")); + g_signal_connect(G_OBJECT(los_wav_but), "clicked", + G_CALLBACK(select_file_cb), los_wav); + gtk_grid_attach(GTK_GRID(table), los_wav_but, 3, 14, 1, 1); if (conf->name != NULL) update_widgets(conf); @@ -560,6 +697,10 @@ static gboolean apply_changes(radio_conf_t * conf) conf->signal_aos = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos)); conf->signal_los = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos)); + /* AOS / LOS elevation */ + conf->aos_el = gtk_spin_button_get_value(GTK_SPIN_BUTTON(aos_el)); + conf->los_el = gtk_spin_button_get_value(GTK_SPIN_BUTTON(los_el)); + /* AOS command */ if (conf->aos_command) g_free(conf->aos_command); @@ -572,6 +713,18 @@ static gboolean apply_changes(radio_conf_t * conf) conf->los_command = g_strdup(gtk_entry_get_text(GTK_ENTRY(los_command))); + /* AOS .wav file */ + if (conf->aos_wav) + g_free(conf->aos_wav); + + conf->aos_wav = g_strdup(gtk_entry_get_text(GTK_ENTRY(aos_wav))); + + /* LOS .wav file */ + if (conf->los_wav) + g_free(conf->los_wav); + + conf->los_wav = g_strdup(gtk_entry_get_text(GTK_ENTRY(los_wav))); + return TRUE; } diff --git a/src/sat-pref-rig.c b/src/sat-pref-rig.c index f9016fcc..b7d26113 100644 --- a/src/sat-pref-rig.c +++ b/src/sat-pref-rig.c @@ -62,8 +62,12 @@ static GtkTreeModel *create_and_fill_model() G_TYPE_DOUBLE, // LO UO G_TYPE_BOOLEAN, // AOS signalling G_TYPE_BOOLEAN, // LOS signalling + G_TYPE_DOUBLE, // AOS elevation + G_TYPE_DOUBLE, // LOS elevation G_TYPE_STRING, // AOS command - G_TYPE_STRING // LOS command + G_TYPE_STRING, // LOS command + G_TYPE_STRING, // AOS .wav + G_TYPE_STRING // LOS .wav ); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), @@ -100,8 +104,12 @@ static GtkTreeModel *create_and_fill_model() RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_ELEVATION, conf.aos_el, + RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_WAV, conf.aos_wav, + RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); sat_log_log(SAT_LOG_LEVEL_DEBUG, @@ -355,6 +363,28 @@ static void render_vfo(GtkTreeViewColumn * col, g_free(buff); } +/** + * Render angle + * + * This function is used to render an elevation angle in degrees. + */ +static void render_angle(GtkTreeViewColumn * col, + GtkCellRenderer * renderer, + GtkTreeModel * model, + GtkTreeIter * iter, + gpointer column) +{ + gdouble number; + gchar *buff; + guint coli = GPOINTER_TO_UINT(column); + + gtk_tree_model_get(model, iter, coli, &number, -1); + + buff = g_strdup_printf("%.1f deg", number); + g_object_set(renderer, "text", buff, NULL); + g_free(buff); +} + static void render_signal(GtkTreeViewColumn * col, GtkCellRenderer * renderer, GtkTreeModel * model, GtkTreeIter * iter, gpointer column) @@ -398,7 +428,13 @@ static void edit_cb(GtkWidget * button, gpointer data) .lo = 0.0, .loup = 0.0, .signal_aos = FALSE, - .signal_los = FALSE + .signal_los = FALSE, + .aos_el = 0.0, + .los_el = 0.0, + .aos_command = NULL, + .los_command = NULL, + .aos_wav = NULL, + .los_wav = NULL }; /* If there are no entries, we have a bug since the button should @@ -431,8 +467,12 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOUP, &conf.loup, RIG_LIST_COL_SIGAOS, &conf.signal_aos, RIG_LIST_COL_SIGLOS, &conf.signal_los, + RIG_LIST_COL_AOS_ELEVATION, &conf.aos_el, + RIG_LIST_COL_LOS_ELEVATION, &conf.los_el, RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + RIG_LIST_COL_AOS_WAV, &conf.aos_wav, + RIG_LIST_COL_LOS_WAV, &conf.los_wav, -1); } else @@ -470,8 +510,12 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_ELEVATION, conf.aos_el, + RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_WAV, conf.aos_wav, + RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); } @@ -619,6 +663,30 @@ static void create_rig_list() (RIG_LIST_COL_SIGLOS), NULL); gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + /* AOS elevation */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("AOS Elevation"), + renderer, + "text", + RIG_LIST_COL_AOS_ELEVATION, + NULL); + gtk_tree_view_column_set_cell_data_func(column, renderer, render_angle, + GUINT_TO_POINTER + (RIG_LIST_COL_AOS_ELEVATION), NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* LOS elevation */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("LOS Elevation"), + renderer, + "text", + RIG_LIST_COL_LOS_ELEVATION, + NULL); + gtk_tree_view_column_set_cell_data_func(column, renderer, render_angle, + GUINT_TO_POINTER + (RIG_LIST_COL_LOS_ELEVATION), NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + /* AOS command */ renderer = gtk_cell_renderer_text_new(); @@ -639,6 +707,23 @@ static void create_rig_list() gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + /* AOS .wav file */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("AOS Audio File"), + renderer, + "text", + RIG_LIST_COL_AOS_WAV, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* LOS .wav file */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("LOS Audio File"), + renderer, + "text", + RIG_LIST_COL_LOS_WAV, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); g_signal_connect(riglist, "row-activated", G_CALLBACK(row_activated_cb), @@ -723,8 +808,12 @@ static void add_cb(GtkWidget * button, gpointer data) .loup = 0.0, .signal_aos = FALSE, .signal_los = FALSE, + .aos_el = 0.0, + .los_el = 0.0, .aos_command = NULL, - .los_command = NULL + .los_command = NULL, + .aos_wav = NULL, + .los_wav = NULL }; /* run rig conf editor */ @@ -748,8 +837,12 @@ static void add_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOUP, conf.loup, RIG_LIST_COL_SIGAOS, conf.signal_aos, RIG_LIST_COL_SIGLOS, conf.signal_los, + RIG_LIST_COL_AOS_ELEVATION, conf.aos_el, + RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_WAV, conf.aos_wav, + RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); g_free(conf.name); @@ -852,8 +945,12 @@ void sat_pref_rig_ok() .loup = 0.0, .signal_aos = FALSE, .signal_los = FALSE, + .aos_el = 0.0, + .los_el = 0.0, .aos_command = NULL, - .los_command = NULL + .los_command = NULL, + .aos_wav = NULL, + .los_wav = NULL }; /* delete all .rig files */ @@ -900,8 +997,12 @@ void sat_pref_rig_ok() RIG_LIST_COL_LOUP, &conf.loup, RIG_LIST_COL_SIGAOS, &conf.signal_aos, RIG_LIST_COL_SIGLOS, &conf.signal_los, + RIG_LIST_COL_AOS_ELEVATION, &conf.aos_el, + RIG_LIST_COL_LOS_ELEVATION, &conf.los_el, RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + RIG_LIST_COL_AOS_WAV, &conf.aos_wav, + RIG_LIST_COL_LOS_WAV, &conf.los_wav, -1); radio_conf_save(&conf); diff --git a/win32/Makefile b/win32/Makefile index 69cd8c6b..685e28bd 100644 --- a/win32/Makefile +++ b/win32/Makefile @@ -1,16 +1,12 @@ # Makefile for cross-compiling gpredict for win32 on Linux, # using mingw compiler -# configure pkg-config, version from git include config.mk # version string in C code VERSION_SHORT = $(GITMAJ).$(GITMIN).$(GITBLD) VERSION_LONG = $(GITMAJ).$(GITMIN).0.$(GITBLD) -# cross-compiler prefix -MGW_PREFIX=i686-w64-mingw32- - # directories topsrc = .. gpreddir = $(topsrc)/src @@ -18,7 +14,7 @@ sgpsdpdir = $(gpreddir)/sgpsdp nxjsondir = $(gpreddir)/nxjson # tools -CC = $(MGW_PREFIX)gcc -Wall -O2 -mms-bitfields -DWIN32 -m32 +CC = $(MGW_PREFIX)gcc -Wall -O2 -mms-bitfields -DWIN32 AS = $(MGW_PREFIX)as DLLWRAP = $(MGW_PREFIX)dllwrap DLLWRAP_FLAGS = --as=$(AS) --export-all --driver-name $(CC) -s @@ -28,16 +24,18 @@ RCFLAGS = --define __WIN32__ --define __WIN95__ --define MSRC --define __GNUWIN3 --define VERMAJ=$(GITMAJ) --define VERMIN=$(GITMIN) --define VERBLD=$(GITBLD) # libraries -GTKLIBS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --define-prefix --libs gtk+-3.0) -GLIBLIB := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --define-prefix --libs glib-2.0 gthread-2.0) -GOOLIBS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --define-prefix --libs goocanvas-2.0) -GUI_LIBS = $(GTKLIBS) $(GLIBLIB) $(GOOLIBS) libsgpsdp.lib -lwinmm -lwininet -lws2_32 +GTKLIBS := $(shell pkg-config --define-prefix --libs gtk+-3.0) +GLIBLIB := $(shell pkg-config --define-prefix --libs glib-2.0 gthread-2.0) +GOOLIBS := $(shell pkg-config --define-prefix --libs goocanvas-2.0) +GSTLIBS := $(shell pkg-config --define-prefix --libs gstreamer-1.0) +GUI_LIBS = $(GTKLIBS) $(GLIBLIB) $(GOOLIBS) $(GSTLIBS) libsgpsdp.lib -lwinmm -lwininet -lws2_32 LIBS = -lm # flags/defines -GTK_CFLAGS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --define-prefix --cflags gtk+-3.0) -GOO_CFLAGS := $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --define-prefix --cflags goocanvas-2.0) -CFLAGS = -DVERSION_SHORT=$(VERSION_SHORT) -DHAVE_CONFIG_H -DPACKAGE_LOCALE_DIR=\"/\" -I. -I.. -I$(gpreddir) $(GOO_CFLAGS) +GTK_CFLAGS := $(shell pkg-config --define-prefix --cflags gtk+-3.0) +GOO_CFLAGS := $(shell pkg-config --define-prefix --cflags goocanvas-2.0) +GST_CFLAGS := $(shell pkg-config --define-prefix --cflags gstreamer-1.0) +CFLAGS = -DVERSION_SHORT=$(VERSION_SHORT) -DHAVE_CONFIG_H -DPACKAGE_LOCALE_DIR=\"/\" -I. -I.. -I$(gpreddir) $(GOO_CFLAGS) $(GST_CFLAGS) # source paths vpath %.c $(sgpsdpdir) @@ -67,8 +65,8 @@ NXJSONSRC = \ NXJSONOBJ = $(NXJSONSRC:.c=.o) GOOCANVSRC = \ - goocanvasatk.c \ - goocanvasellipse.c \ + goocanvasatk.c \ + goocanvasellipse.c \ goocanvasenumtypes.c \ goocanvasgroup.c \ goocanvasimage.c \ @@ -97,6 +95,7 @@ GPREDICTSRC = \ first-time.c \ gpredict-help.c \ gpredict-utils.c \ + gtk-audio.c \ gtk-azel-plot.c \ gtk-event-list.c \ gtk-event-list-popup.c \ @@ -202,11 +201,21 @@ gpredict-$(GITMAJ).$(GITMIN).$(GITBLD).zip: all cp -rp ../po gpredict/share/gpredict mkdir -p gpredict/$(LOADERS) cp -p loaders.cache gpredict/$(LOADERS) + mkdir -p gpredict/$(LOADERS)/loaders + cp $(LOADER_DLLS)/libpixbufloader-jpeg*.dll gpredict/$(LOADERS)/loaders + cp $(LOADER_DLLS)/libpixbufloader-png*.dll gpredict/$(LOADERS)/loaders + cp $(LOADER_DLLS)/libpixbufloader-svg*.dll gpredict/$(LOADERS)/loaders + cp -rp $(MINGW_ROOT)/lib/gstreamer-1.0 gpredict/lib cp -rp $(GTKETC) gpredict mkdir -p gpredict/share/glib-2.0 cp -rp $(SCHEMAS) gpredict/share/glib-2.0 - zip -r $@ gpredict - rm -rf gpredict + mkdir -p gpredict/share/icons/Adwaita + cp -rp $(ADWAITA)/scalable gpredict/share/icons/Adwaita/ + cp -rp $(ADWAITA)/scalable-up-to-32 gpredict/share/icons/Adwaita/ + cp -rp $(ADWAITA)/index.theme gpredict/share/icons/Adwaita/ + cp -rp $(ADWAITA)/icon-theme.cache gpredict/share/icons/Adwaita/ + #zip -r $@ gpredict + #rm -rf gpredict # Use -mconsole to always open a console window when gpredicxt is started # Use -mwindows for no console diff --git a/win32/build-config.h b/win32/build-config.h index 4408fe85..13dabbaf 100644 --- a/win32/build-config.h +++ b/win32/build-config.h @@ -98,3 +98,5 @@ #undef HAS_LIBGPS +/* Define if libgstreamer is available */ +#define HAS_LIBGSTREAMER 1 diff --git a/win32/config.mk b/win32/config.mk index 7ffbb17e..3ed7a29f 100644 --- a/win32/config.mk +++ b/win32/config.mk @@ -1,33 +1,23 @@ -# Configuration variables governing the build of gpredict for win32 +# Configuration variables governing the build of gpredict for win32 (or win64) -# pkgconfig path, assumes goocanvas-2.0.2 and gtk+-3.10.4 win32 packages -# unpacked and paths adjusted (pkg-config files) at the same folder level -# as gpredict. Downloads used: -# -# http://ftp.gnome.org/pub/GNOME/binaries/win32/goocanvas/2.0/ -# download both: goocanvas-2.0.2-win32.zip, goocanvas-dev-2.0.2-win32.zip -# -# http://win32builder.gnome.org/gtk+-bundle_3.10.4-20131202_win32.zip -# NB: I had to create new .pc files for both gtk+-3.0.pc and gdk.pc -# (thanks GNOME for missing crucial bits out of builds). My versions -# are in this folder, and will need unpack paths inserting. - -MINGW_ROOT=../../mingw32 -#MINGW_ROOT=../../../tmp/mingw32 - -PKG_CONFIG_PATH = "$(abspath $(MINGW_ROOT)/lib/pkgconfig)" +# Choose between 32-bit or 64-bit compiler +MINGW_ROOT=/mingw32 +MGW_PREFIX=i686-w64-mingw32- +#MINGW_ROOT=/mingw64 +#MGW_PREFIX=x86_64-w64-mingw32- # binary dependencies to be deployed with gpredict.exe BINDEPS = \ - $(wildcard $(MINGW_ROOT)/bin/*.dll) \ - $(MINGW_ROOT)/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll + $(wildcard $(MINGW_ROOT)/bin/*.dll) -# where to put the loaders.cache file +# where to put the loaders.cache file and dlls LOADERS = lib/gdk-pixbuf-2.0/2.10.0 +LOADER_DLLS = $(MINGW_ROOT)/$(LOADERS)/loaders/ # other miscellaneous folders to deploy with the binary GTKETC = $(MINGW_ROOT)/etc SCHEMAS = $(MINGW_ROOT)/share/glib-2.0/schemas +ADWAITA = $(MINGW_ROOT)/share/icons/Adwaita # Autoversioning from nearest git tag, assumes v. tag format. diff --git a/win32/loaders.cache b/win32/loaders.cache index 80c0b05b..c3c4e0fe 100644 --- a/win32/loaders.cache +++ b/win32/loaders.cache @@ -1,14 +1,23 @@ -# GdkPixbuf Image Loader Modules file -# Automatically generated file, do not edit -# Created by gdk-pixbuf-query-loaders.exe from gdk-pixbuf-2.30.1 -# -# LoaderDir = Z:\srv\win32builder\fixed_3104\build\win32/lib/gdk-pixbuf-2.0/2.10.0/loaders -# -"libpixbufloader-svg.dll" -"svg" 2 "gdk-pixbuf" "Scalable Vector Graphics" "LGPL" -"image/svg+xml" "image/svg" "image/svg-xml" "image/vnd.adobe.svg+xml" "text/xml-svg" "image/svg+xml-compressed" "" -"svg" "svgz" "svg.gz" "" -" Date: Fri, 21 Feb 2020 12:17:25 +0000 Subject: [PATCH 3/4] Add ability to run applications (shell commands) on AOS and LOS. --- src/gtk-rig-ctrl.c | 50 ++++++++++++++++++++++++++++++-- src/radio-conf.c | 11 +++++++ src/radio-conf.h | 3 ++ src/sat-pref-rig-data.h | 2 ++ src/sat-pref-rig-editor.c | 61 +++++++++++++++++++++++++++++++++++---- src/sat-pref-rig.c | 37 ++++++++++++++++++++++++ 6 files changed, 155 insertions(+), 9 deletions(-) diff --git a/src/gtk-rig-ctrl.c b/src/gtk-rig-ctrl.c index ca9a51b6..95066af4 100644 --- a/src/gtk-rig-ctrl.c +++ b/src/gtk-rig-ctrl.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include /* NETWORK */ #ifndef WIN32 @@ -2375,6 +2377,7 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) { gboolean retcode = TRUE; gchar retbuf[10]; + int sysret; /* Don't check tracking, as we want AOS/LOS signalling, even if not adjusting for doppler, which we may not want to do for some demodulators */ @@ -2384,9 +2387,17 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) && ctrl->target->el >= ctrl->conf->aos_el) { /* AOS has occurred */ - if (ctrl->conf->signal_aos && ctrl->conf->aos_wav) + + /* Play audio waveform */ + if (ctrl->conf->signal_aos && ctrl->conf->aos_wav + && (strlen(ctrl->conf->aos_wav) > 0)) + { audio_play_uri(ctrl->conf->aos_wav); - if (ctrl->conf->signal_aos && ctrl->conf->aos_command) + } + + /* Send commands to radio */ + if (ctrl->conf->signal_aos && ctrl->conf->aos_command + && (strlen(ctrl->conf->aos_command) > 0)) { retcode &= send_rigctld_commands(ctrl, ctrl->sock, ctrl->conf->aos_command, @@ -2401,13 +2412,33 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) retbuf, sizeof(retbuf)); } } + + /* Run application */ + if (ctrl->conf->signal_aos && ctrl->conf->aos_app + && (strlen(ctrl->conf->aos_app) > 0)) + { + sysret = system(ctrl->conf->aos_app); + if (sysret != 0) + { + sat_log_log(SAT_LOG_LEVEL_ERROR, + _("AOS application \"%s\" returned %d"), + ctrl->conf->aos_app, sysret); + } + } } else if (ctrl->prev_ele >= ctrl->conf->los_el && ctrl->target->el < ctrl->conf->los_el) { /* LOS has occurred */ - if (ctrl->conf->signal_los && ctrl->conf->los_wav) + + /* Play audio waveform */ + if (ctrl->conf->signal_los && ctrl->conf->los_wav + && (strlen(ctrl->conf->los_wav) > 0)) + { audio_play_uri(ctrl->conf->los_wav); + } + + /* Send commands to radio */ if (ctrl->conf->signal_los && ctrl->conf->los_command) { retcode &= send_rigctld_commands(ctrl, ctrl->sock, @@ -2423,6 +2454,19 @@ static gboolean check_aos_los(GtkRigCtrl * ctrl) retbuf, sizeof(retbuf)); } } + + /* Run application */ + if (ctrl->conf->signal_los && ctrl->conf->los_app + && (strlen(ctrl->conf->los_app) > 0)) + { + sysret = system(ctrl->conf->los_app); + if (sysret != 0) + { + sat_log_log(SAT_LOG_LEVEL_ERROR, + _("LOS application \"%s\" returned %d"), + ctrl->conf->los_app, sysret); + } + } } } diff --git a/src/radio-conf.c b/src/radio-conf.c index eae7a0f8..ad1bc580 100644 --- a/src/radio-conf.c +++ b/src/radio-conf.c @@ -49,6 +49,8 @@ #define KEY_LOS_ELEVATION "LOS_ELEVATION" #define KEY_AOS_COMMAND "AOS_COMMAND" #define KEY_LOS_COMMAND "LOS_COMMAND" +#define KEY_AOS_APP "AOS_APP" +#define KEY_LOS_APP "LOS_APP" #define KEY_AOS_WAV "AOS_WAV" #define KEY_LOS_WAV "LOS_WAV" @@ -251,6 +253,10 @@ gboolean radio_conf_read(radio_conf_t * conf) conf->aos_command = g_key_file_get_string(cfg, GROUP, KEY_AOS_COMMAND, NULL); conf->los_command = g_key_file_get_string(cfg, GROUP, KEY_LOS_COMMAND, NULL); + /* AOS and LOS applications */ + conf->aos_app = g_key_file_get_string(cfg, GROUP, KEY_AOS_APP, NULL); + conf->los_app = g_key_file_get_string(cfg, GROUP, KEY_LOS_APP, NULL); + /* AOS and LOS .wav files */ conf->aos_wav = g_key_file_get_string(cfg, GROUP, KEY_AOS_WAV, NULL); conf->los_wav = g_key_file_get_string(cfg, GROUP, KEY_LOS_WAV, NULL); @@ -315,6 +321,11 @@ void radio_conf_save(radio_conf_t * conf) if (conf->los_command) g_key_file_set_string(cfg, GROUP, KEY_LOS_COMMAND, conf->los_command); + if (conf->aos_app) + g_key_file_set_string(cfg, GROUP, KEY_AOS_APP, conf->aos_app); + if (conf->los_app) + g_key_file_set_string(cfg, GROUP, KEY_LOS_APP, conf->los_app); + if (conf->aos_wav) g_key_file_set_string(cfg, GROUP, KEY_AOS_WAV, conf->aos_wav); if (conf->los_wav) diff --git a/src/radio-conf.h b/src/radio-conf.h index b112943e..92d9bd01 100644 --- a/src/radio-conf.h +++ b/src/radio-conf.h @@ -79,6 +79,9 @@ typedef struct { gchar *aos_command; /*!< Commands to send to radio on AOS */ gchar *los_command; /*!< Commands to send to radio on LOS */ + gchar *aos_app; /*!< Application to run on AOS */ + gchar *los_app; /*!< Application to run on LOS */ + gchar *aos_wav; /*!< Audio file (E.g. .wav) to play on AOS */ gchar *los_wav; /*!< Audio file (E.g. .wav) to play on LOS */ } radio_conf_t; diff --git a/src/sat-pref-rig-data.h b/src/sat-pref-rig-data.h index f4f117d0..be75e90d 100644 --- a/src/sat-pref-rig-data.h +++ b/src/sat-pref-rig-data.h @@ -44,6 +44,8 @@ typedef enum { RIG_LIST_COL_LOS_ELEVATION, /*!< LOS elevation */ RIG_LIST_COL_AOS_COMMAND, /*!< AOS command */ RIG_LIST_COL_LOS_COMMAND, /*!< LOS command */ + RIG_LIST_COL_AOS_APP, /*!< AOS application */ + RIG_LIST_COL_LOS_APP, /*!< LOS application */ RIG_LIST_COL_AOS_WAV, /*!< AOS audio file */ RIG_LIST_COL_LOS_WAV, /*!< LOS audio file */ RIG_LIST_COL_NUM /*!< The number of fields in the list. */ diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c index c5a1380a..85b1d3c3 100644 --- a/src/sat-pref-rig-editor.c +++ b/src/sat-pref-rig-editor.c @@ -47,6 +47,8 @@ static GtkWidget *aos_el; /* AOS elevation */ static GtkWidget *los_el; /* LOS elevation */ static GtkWidget *aos_command; /* AOS command */ static GtkWidget *los_command; /* LOS command */ +static GtkWidget *aos_app; /* AOS application */ +static GtkWidget *los_app; /* LOS application */ static GtkWidget *aos_wav; /* AOS audio file */ static GtkWidget *los_wav; /* LOS audio file */ static GtkWidget *aos_wav_but; /* AOS audio file select button */ @@ -70,6 +72,8 @@ static void clear_widgets() gtk_spin_button_set_value(GTK_SPIN_BUTTON(los_el), 0.0); gtk_entry_set_text(GTK_ENTRY(aos_command), ""); gtk_entry_set_text(GTK_ENTRY(los_command), ""); + gtk_entry_set_text(GTK_ENTRY(aos_app), ""); + gtk_entry_set_text(GTK_ENTRY(los_app), ""); gtk_entry_set_text(GTK_ENTRY(aos_wav), ""); gtk_entry_set_text(GTK_ENTRY(los_wav), ""); } @@ -132,6 +136,14 @@ static void update_widgets(radio_conf_t * conf) gtk_entry_set_text(GTK_ENTRY(los_command), conf->los_command); gtk_widget_set_sensitive(los_command, conf->signal_los); + /* AOS / LOS applications */ + if (conf->aos_app) + gtk_entry_set_text(GTK_ENTRY(aos_app), conf->aos_app); + gtk_widget_set_sensitive(aos_app, conf->signal_aos); + if (conf->los_app) + gtk_entry_set_text(GTK_ENTRY(los_app), conf->los_app); + gtk_widget_set_sensitive(los_app, conf->signal_los); + /* AOS / LOS .wav files */ if (conf->aos_wav) gtk_entry_set_text(GTK_ENTRY(aos_wav), conf->aos_wav); @@ -269,6 +281,7 @@ static void aos_changed(GtkWidget * widget, gpointer data) active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sigaos)); gtk_widget_set_sensitive(aos_el, active); gtk_widget_set_sensitive(aos_command, active); + gtk_widget_set_sensitive(aos_app, active); gtk_widget_set_sensitive(aos_wav, active); } @@ -280,6 +293,7 @@ static void los_changed(GtkWidget * widget, gpointer data) active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(siglos)); gtk_widget_set_sensitive(los_el, active); gtk_widget_set_sensitive(los_command, active); + gtk_widget_set_sensitive(los_app, active); gtk_widget_set_sensitive(los_wav, active); } @@ -590,37 +604,60 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) "e.g. set_powerstat 0")); gtk_grid_attach(GTK_GRID(table), los_command, 1, 12, 3, 1); + /* AOS application */ + label = gtk_label_new(_("AOS Application")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 13, 1, 1); + + aos_app = gtk_entry_new(); + gtk_widget_set_tooltip_text(aos_app, + _("Enter application (shell command) to run on AOS.\n" + "To run asychronously:\n" + " on Linux, add & at the end\n" + " on Windows, use start at the begining")); + gtk_grid_attach(GTK_GRID(table), aos_app, 1, 13, 3, 1); + + /* LOS application */ + label = gtk_label_new(_("LOS Application")); + g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); + gtk_grid_attach(GTK_GRID(table), label, 0, 14, 1, 1); + + los_app = gtk_entry_new(); + gtk_widget_set_tooltip_text(los_app, + _("Enter application (shell command) to run LOS.")); + gtk_grid_attach(GTK_GRID(table), los_app, 1, 14, 3, 1); + /* AOS audio file */ label = gtk_label_new(_("AOS Audio File")); g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 13, 1, 1); + gtk_grid_attach(GTK_GRID(table), label, 0, 15, 1, 1); aos_wav = gtk_entry_new(); gtk_widget_set_tooltip_text(aos_wav, _("Audio (E.g. .wav) file to play on AOS")); - gtk_grid_attach(GTK_GRID(table), aos_wav, 1, 13, 2, 1); + gtk_grid_attach(GTK_GRID(table), aos_wav, 1, 15, 2, 1); aos_wav_but = gtk_button_new_with_label(_("Select")); gtk_widget_set_tooltip_text(aos_wav_but, _("Click to select a file")); g_signal_connect(G_OBJECT(aos_wav_but), "clicked", G_CALLBACK(select_file_cb), aos_wav); - gtk_grid_attach(GTK_GRID(table), aos_wav_but, 3, 13, 1, 1); + gtk_grid_attach(GTK_GRID(table), aos_wav_but, 3, 15, 1, 1); /* LOS audio file */ label = gtk_label_new(_("LOS Audio File")); g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 14, 1, 1); + gtk_grid_attach(GTK_GRID(table), label, 0, 16, 1, 1); los_wav = gtk_entry_new(); gtk_widget_set_tooltip_text(los_wav, _("Audio (E.g. .wav) file to play on LOS")); - gtk_grid_attach(GTK_GRID(table), los_wav, 1, 14, 2, 1); + gtk_grid_attach(GTK_GRID(table), los_wav, 1, 16, 2, 1); los_wav_but = gtk_button_new_with_label(_("Select")); gtk_widget_set_tooltip_text(los_wav_but, _("Click to select a file")); g_signal_connect(G_OBJECT(los_wav_but), "clicked", G_CALLBACK(select_file_cb), los_wav); - gtk_grid_attach(GTK_GRID(table), los_wav_but, 3, 14, 1, 1); + gtk_grid_attach(GTK_GRID(table), los_wav_but, 3, 16, 1, 1); if (conf->name != NULL) update_widgets(conf); @@ -713,6 +750,18 @@ static gboolean apply_changes(radio_conf_t * conf) conf->los_command = g_strdup(gtk_entry_get_text(GTK_ENTRY(los_command))); + /* AOS application */ + if (conf->aos_app) + g_free(conf->aos_app); + + conf->aos_app = g_strdup(gtk_entry_get_text(GTK_ENTRY(aos_app))); + + /* LOS application */ + if (conf->los_app) + g_free(conf->los_app); + + conf->los_app = g_strdup(gtk_entry_get_text(GTK_ENTRY(los_app))); + /* AOS .wav file */ if (conf->aos_wav) g_free(conf->aos_wav); diff --git a/src/sat-pref-rig.c b/src/sat-pref-rig.c index b7d26113..ce788f70 100644 --- a/src/sat-pref-rig.c +++ b/src/sat-pref-rig.c @@ -66,6 +66,8 @@ static GtkTreeModel *create_and_fill_model() G_TYPE_DOUBLE, // LOS elevation G_TYPE_STRING, // AOS command G_TYPE_STRING, // LOS command + G_TYPE_STRING, // AOS application + G_TYPE_STRING, // LOS application G_TYPE_STRING, // AOS .wav G_TYPE_STRING // LOS .wav ); @@ -108,6 +110,8 @@ static GtkTreeModel *create_and_fill_model() RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_APP, conf.aos_app, + RIG_LIST_COL_LOS_APP, conf.los_app, RIG_LIST_COL_AOS_WAV, conf.aos_wav, RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); @@ -433,6 +437,8 @@ static void edit_cb(GtkWidget * button, gpointer data) .los_el = 0.0, .aos_command = NULL, .los_command = NULL, + .aos_app = NULL, + .los_app = NULL, .aos_wav = NULL, .los_wav = NULL }; @@ -471,6 +477,8 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOS_ELEVATION, &conf.los_el, RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + RIG_LIST_COL_AOS_APP, &conf.aos_app, + RIG_LIST_COL_LOS_APP, &conf.los_app, RIG_LIST_COL_AOS_WAV, &conf.aos_wav, RIG_LIST_COL_LOS_WAV, &conf.los_wav, -1); @@ -514,6 +522,8 @@ static void edit_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_APP, conf.aos_app, + RIG_LIST_COL_LOS_APP, conf.los_app, RIG_LIST_COL_AOS_WAV, conf.aos_wav, RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); @@ -707,6 +717,25 @@ static void create_rig_list() gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + /* AOS application */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("AOS Application"), + renderer, + "text", + RIG_LIST_COL_AOS_APP, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* LOS application */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("LOS Application"), + renderer, + "text", + RIG_LIST_COL_LOS_APP, + NULL); + gtk_tree_view_insert_column(GTK_TREE_VIEW(riglist), column, -1); + + /* AOS .wav file */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("AOS Audio File"), @@ -812,6 +841,8 @@ static void add_cb(GtkWidget * button, gpointer data) .los_el = 0.0, .aos_command = NULL, .los_command = NULL, + .aos_app = NULL, + .los_app = NULL, .aos_wav = NULL, .los_wav = NULL }; @@ -841,6 +872,8 @@ static void add_cb(GtkWidget * button, gpointer data) RIG_LIST_COL_LOS_ELEVATION, conf.los_el, RIG_LIST_COL_AOS_COMMAND, conf.aos_command, RIG_LIST_COL_LOS_COMMAND, conf.los_command, + RIG_LIST_COL_AOS_APP, conf.aos_app, + RIG_LIST_COL_LOS_APP, conf.los_app, RIG_LIST_COL_AOS_WAV, conf.aos_wav, RIG_LIST_COL_LOS_WAV, conf.los_wav, -1); @@ -949,6 +982,8 @@ void sat_pref_rig_ok() .los_el = 0.0, .aos_command = NULL, .los_command = NULL, + .aos_app = NULL, + .los_app = NULL, .aos_wav = NULL, .los_wav = NULL }; @@ -1001,6 +1036,8 @@ void sat_pref_rig_ok() RIG_LIST_COL_LOS_ELEVATION, &conf.los_el, RIG_LIST_COL_AOS_COMMAND, &conf.aos_command, RIG_LIST_COL_LOS_COMMAND, &conf.los_command, + RIG_LIST_COL_AOS_APP, &conf.aos_app, + RIG_LIST_COL_LOS_APP, &conf.los_app, RIG_LIST_COL_AOS_WAV, &conf.aos_wav, RIG_LIST_COL_LOS_WAV, &conf.los_wav, -1); From f359d8bfabc845330c706cca0bd70d40b7752645 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Sun, 23 Feb 2020 14:29:42 +0000 Subject: [PATCH 4/4] Fix sensitivity of AOS/LOS fields --- src/sat-pref-rig-editor.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sat-pref-rig-editor.c b/src/sat-pref-rig-editor.c index 85b1d3c3..eabf0a20 100644 --- a/src/sat-pref-rig-editor.c +++ b/src/sat-pref-rig-editor.c @@ -148,9 +148,11 @@ static void update_widgets(radio_conf_t * conf) if (conf->aos_wav) gtk_entry_set_text(GTK_ENTRY(aos_wav), conf->aos_wav); gtk_widget_set_sensitive(aos_wav, conf->signal_aos); + gtk_widget_set_sensitive(aos_wav_but, conf->signal_aos); if (conf->los_wav) gtk_entry_set_text(GTK_ENTRY(los_wav), conf->los_wav); gtk_widget_set_sensitive(los_wav, conf->signal_los); + gtk_widget_set_sensitive(los_wav_but, conf->signal_los); } /* @@ -283,6 +285,7 @@ static void aos_changed(GtkWidget * widget, gpointer data) gtk_widget_set_sensitive(aos_command, active); gtk_widget_set_sensitive(aos_app, active); gtk_widget_set_sensitive(aos_wav, active); + gtk_widget_set_sensitive(aos_wav_but, active); } /* Manage signal LOS change. */ @@ -295,6 +298,7 @@ static void los_changed(GtkWidget * widget, gpointer data) gtk_widget_set_sensitive(los_command, active); gtk_widget_set_sensitive(los_app, active); gtk_widget_set_sensitive(los_wav, active); + gtk_widget_set_sensitive(los_wav_but, active); } /** @@ -590,8 +594,9 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) aos_command = gtk_entry_new(); gtk_widget_set_tooltip_text(aos_command, _("Enter commands to send to the radio on AOS, " - "e.g. set_powerstat 1")); + "e.g. set_powerstat 1 or AOS")); gtk_grid_attach(GTK_GRID(table), aos_command, 1, 11, 3, 1); + gtk_widget_set_sensitive(aos_command, FALSE); /* LOS command */ label = gtk_label_new(_("LOS Command")); @@ -601,8 +606,9 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) los_command = gtk_entry_new(); gtk_widget_set_tooltip_text(los_command, _("Enter commands to send to the radio on LOS, " - "e.g. set_powerstat 0")); + "e.g. set_powerstat 0 or LOS")); gtk_grid_attach(GTK_GRID(table), los_command, 1, 12, 3, 1); + gtk_widget_set_sensitive(los_command, FALSE); /* AOS application */ label = gtk_label_new(_("AOS Application")); @@ -616,6 +622,7 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) " on Linux, add & at the end\n" " on Windows, use start at the begining")); gtk_grid_attach(GTK_GRID(table), aos_app, 1, 13, 3, 1); + gtk_widget_set_sensitive(aos_app, FALSE); /* LOS application */ label = gtk_label_new(_("LOS Application")); @@ -626,6 +633,7 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) gtk_widget_set_tooltip_text(los_app, _("Enter application (shell command) to run LOS.")); gtk_grid_attach(GTK_GRID(table), los_app, 1, 14, 3, 1); + gtk_widget_set_sensitive(los_app, FALSE); /* AOS audio file */ label = gtk_label_new(_("AOS Audio File")); @@ -636,12 +644,14 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) gtk_widget_set_tooltip_text(aos_wav, _("Audio (E.g. .wav) file to play on AOS")); gtk_grid_attach(GTK_GRID(table), aos_wav, 1, 15, 2, 1); + gtk_widget_set_sensitive(aos_wav, FALSE); aos_wav_but = gtk_button_new_with_label(_("Select")); gtk_widget_set_tooltip_text(aos_wav_but, _("Click to select a file")); g_signal_connect(G_OBJECT(aos_wav_but), "clicked", G_CALLBACK(select_file_cb), aos_wav); gtk_grid_attach(GTK_GRID(table), aos_wav_but, 3, 15, 1, 1); + gtk_widget_set_sensitive(aos_wav_but, FALSE); /* LOS audio file */ label = gtk_label_new(_("LOS Audio File")); @@ -652,12 +662,14 @@ static GtkWidget *create_editor_widgets(radio_conf_t * conf) gtk_widget_set_tooltip_text(los_wav, _("Audio (E.g. .wav) file to play on LOS")); gtk_grid_attach(GTK_GRID(table), los_wav, 1, 16, 2, 1); + gtk_widget_set_sensitive(los_wav, FALSE); los_wav_but = gtk_button_new_with_label(_("Select")); gtk_widget_set_tooltip_text(los_wav_but, _("Click to select a file")); g_signal_connect(G_OBJECT(los_wav_but), "clicked", G_CALLBACK(select_file_cb), los_wav); gtk_grid_attach(GTK_GRID(table), los_wav_but, 3, 16, 1, 1); + gtk_widget_set_sensitive(los_wav_but, FALSE); if (conf->name != NULL) update_widgets(conf);