Logs data to files, displays current state in window.
Signed-off-by: Keith Packard <keithp@keithp.com>
--- /dev/null
+*.o
+aoview
-MODULES=gtk+-2.0 libglade-2.0
+MODULES=gtk+-2.0 libglade-2.0 gconf-2.0
INCLUDES=$(shell pkg-config --cflags $(MODULES)) -I..
WARN= -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -fno-strict-aliasing
CFLAGS=$(INCLUDES) -O0 -g $(WARN)
aoview_serial.c \
aoview_monitor.c \
aoview_state.c \
- aoview_convert.c
+ aoview_convert.c \
+ aoview_log.c \
+ aoview_table.c \
+ aoview_util.c
INC = \
aoview.h
</child>
</widget>
</child>
+ <child>
+ <widget class="GtkMenuItem" id="menuitem5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Log</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImageMenuItem" id="ao_log_new">
+ <property name="label" translatable="yes">_New log</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="ao_log_configure">
+ <property name="label" translatable="yes">_Configure Log</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <signal name="activate" handler="gtk_widget_show" object="log_chooser_dialog" after="yes"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
<child>
<widget class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
</packing>
</child>
<child>
- <widget class="GtkTable" id="table1">
+ <widget class="GtkTreeView" id="dataview">
<property name="visible">True</property>
- <property name="n_rows">7</property>
- <property name="n_columns">2</property>
- <child>
- <widget class="GtkLabel" id="altitude_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Altitude</property>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Velocity</property>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Acceleration</property>
- </widget>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Latitude</property>
- </widget>
- <packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Longitude</property>
- </widget>
- <packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label6">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Range</property>
- </widget>
- <packing>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label7">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Direction</property>
- </widget>
- <packing>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="altitude_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">0m</property>
- <property name="selectable">True</property>
- <property name="single_line_mode">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="vel_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">150m/s</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="accel_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">50m/s²</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="lat_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">45° 31' 24''</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="lon_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">122° 40' 34''W</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="range_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">500m</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="direction_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">45°</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
- </packing>
- </child>
+ <property name="can_focus">True</property>
+ <property name="headers_clickable">False</property>
+ <property name="show_expanders">False</property>
+ <property name="enable_grid_lines">both</property>
</widget>
<packing>
<property name="position">1</property>
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
- <widget class="GtkButton" id="connect_button">
- <property name="label" translatable="yes">gtk-connect</property>
+ <widget class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="response_id">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
+ <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="cancel_button">
+ <widget class="GtkButton" id="connect_button">
+ <property name="label" translatable="yes">gtk-connect</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkFileChooserDialog" id="log_chooser_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Configure Log Directory</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <property name="action">select-folder</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="log_configure_cancel">
<property name="label" translatable="yes">gtk-cancel</property>
- <property name="response_id">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_underline">True</property>
<property name="use_stock">True</property>
- <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
+ <signal name="clicked" handler="gtk_widget_hide" object="log_chooser_dialog"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="log_configure_ok">
+ <property name="label" translatable="yes">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
</widget>
<packing>
<property name="expand">False</property>
</widget>
</child>
</widget>
+ <widget class="GtkMessageDialog" id="log_fail_dialog">
+ <property name="border_width">5</property>
+ <property name="type">popup</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">error</property>
+ <property name="buttons">close</property>
+ <property name="text">Cannot create log file</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
</glade-interface>
#ifndef _AOVIEW_H_
#define _AOVIEW_H_
-#include <gtk/gtk.h>
-#include <glade/glade.h>
+#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <assert.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
struct usbdev {
char *sys;
char *tty;
int16_t
aoview_altitude_to_pres(int16_t alt);
+char *
+aoview_fullname (char *dir, char *file);
+
+char *
+aoview_basename(char *file);
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width);
+
+int
+aoview_mkdir(char *dir);
+
+void
+aoview_log_init(GladeXML *xml);
+
+void
+aoview_log_set_serial(int serial);
+
+int
+aoview_log_get_serial(void);
+
+void
+aoview_log_printf(char *format, ...);
+
+void
+aoview_table_start(void);
+
+void
+aoview_table_add_row(char *label, char *format, ...);
+
+void
+aoview_table_finish(void);
+
+void
+aoview_table_init(GladeXML *xml);
+
+
#endif /* _AOVIEW_H_ */
#include <ctype.h>
#include <dirent.h>
-static char *
-fullname (char *dir, char *file)
-{
- char *new;
- int dlen = strlen (dir);
- int flen = strlen (file);
- int slen = 0;
-
- if (dir[dlen-1] != '/')
- slen = 1;
- new = malloc (dlen + slen + flen + 1);
- if (!new)
- return 0;
- strcpy(new, dir);
- if (slen)
- strcat (new, "/");
- strcat(new, file);
- return new;
-}
-
static char *
load_string(char *dir, char *file)
{
- char *full = fullname(dir, file);
+ char *full = aoview_fullname(dir, file);
char line[4096];
char *r;
FILE *f;
return i;
}
-static char *
-basename(char *file)
-{
- char *b;
-
- b = strrchr(file, '/');
- if (!b)
- return file;
- return b + 1;
-}
-
static int
dir_filter_tty(const struct dirent *d)
{
int ntty;
char *tty;
- base = basename(sys);
+ base = aoview_basename(sys);
num_configs = load_hex(sys, "bNumConfigurations");
num_interfaces = load_hex(sys, "bNumInterfaces");
for (config = 1; config <= num_configs; config++) {
for (interface = 0; interface < num_interfaces; interface++) {
sprintf(endpoint_base, "%s:%d.%d",
base, config, interface);
- endpoint_full = fullname(sys, endpoint_base);
+ endpoint_full = aoview_fullname(sys, endpoint_base);
ntty = scandir(endpoint_full, &namelist,
dir_filter_tty,
alphasort);
free(endpoint_full);
if (ntty) {
- tty = fullname("/dev", namelist[0]->d_name + 4);
+ tty = aoview_fullname("/dev", namelist[0]->d_name + 4);
free(namelist);
return tty;
}
if (!n)
return 0;
for (e = 0; e < n; e++) {
- dir = fullname(USB_DEVICES, ents[e]->d_name);
+ dir = aoview_fullname(USB_DEVICES, ents[e]->d_name);
dev = usb_scan_device(dir);
free(dir);
if (dev->idVendor == 0xfffe && dev->tty) {
#include "aoview.h"
-static GtkTreeViewColumn *
-add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
-{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
-
- renderer = gtk_cell_renderer_text_new ();
- g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
- g_object_set(renderer, "width-chars", width, NULL);
- column = gtk_tree_view_column_new_with_attributes (title, renderer,
- "text", model_column,
- NULL);
- gtk_tree_view_column_set_resizable (column, FALSE);
- gtk_tree_view_append_column (view, column);
-
- return column;
-}
-
-
-
static void
aoview_dev_dialog_map(GtkWidget *widget, gpointer data)
{
dev_list = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dev_list"));
assert(dev_list);
- add_plain_text_column(dev_list, _("Product"), 0, 16);
- add_plain_text_column(dev_list, _("Serial"), 1, 8);
- add_plain_text_column(dev_list, _("Device"), 2, 13);
+ aoview_add_plain_text_column(dev_list, _("Product"), 0, 16);
+ aoview_add_plain_text_column(dev_list, _("Serial"), 1, 8);
+ aoview_add_plain_text_column(dev_list, _("Device"), 2, 13);
dev_selection = gtk_tree_view_get_selection(dev_list);
gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE);
--- /dev/null
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+#define LOG_DIR_PATH "/apps/aoview/log_dir"
+#define DEFAULT_LOG "AltOS"
+
+static char *aoview_log_dir;
+static FILE *aoview_log_file;
+static int aoview_log_serial;
+static int aoview_log_sequence;
+static GtkMessageDialog *log_fail_dialog;
+static int aoview_log_failed;
+
+static void
+aoview_log_save_conf(void)
+{
+ GConfClient *gconf_client;
+
+ gconf_client = gconf_client_get_default();
+ if (gconf_client)
+ {
+ gconf_client_set_string(gconf_client,
+ LOG_DIR_PATH,
+ aoview_log_dir,
+ NULL);
+ g_object_unref(G_OBJECT(gconf_client));
+ }
+}
+
+static void
+aoview_log_configure(GtkWidget *widget, gpointer data)
+{
+ GtkFileChooser *chooser = data;
+ aoview_log_dir = gtk_file_chooser_get_filename(chooser);
+ aoview_log_save_conf();
+ gtk_widget_hide(GTK_WIDGET(chooser));
+}
+
+static void
+aoview_log_new(void)
+{
+ if (aoview_log_file) {
+ fclose(aoview_log_file);
+ aoview_log_file = NULL;
+ }
+ aoview_log_failed = 0;
+}
+
+static void
+aoview_log_new_item(GtkWidget *widget, gpointer data)
+{
+ aoview_log_new();
+}
+
+void
+aoview_log_set_serial(int serial)
+{
+ aoview_log_serial = serial;
+}
+
+int
+aoview_log_get_serial(void)
+{
+ return aoview_log_serial;
+}
+
+static void
+aoview_log_open_failed(char *name)
+{
+ char *utf8_file;
+ utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+ if (!utf8_file)
+ utf8_file = name;
+ gtk_message_dialog_format_secondary_text(log_fail_dialog,
+ "\"%s\"", utf8_file);
+ if (utf8_file != name)
+ g_free(utf8_file);
+ gtk_widget_show(GTK_WIDGET(log_fail_dialog));
+ aoview_log_failed = 1;
+}
+
+static void
+aoview_log_start(void)
+{
+ if (!aoview_log_file) {
+ char base[50];
+ struct tm tm;
+ time_t now;
+ char *full;
+ int r;
+
+ now = time(NULL);
+ (void) localtime_r(&now, &tm);
+ aoview_mkdir(aoview_log_dir);
+ for (;;) {
+ sprintf(base, "%04d-%02d-%02d-serial-%03d-flight-%03d.log",
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ aoview_log_serial,
+ aoview_log_sequence);
+ full = aoview_fullname(aoview_log_dir, base);
+ r = access(full, F_OK);
+ if (r < 0) {
+ aoview_log_file = fopen(full, "w");
+ if (!aoview_log_file)
+ aoview_log_open_failed(full);
+ else
+ setlinebuf(aoview_log_file);
+ free(full);
+ break;
+ }
+ free (full);
+ aoview_log_sequence++;
+ }
+ }
+}
+
+void
+aoview_log_printf(char *format, ...)
+{
+ va_list ap;
+
+ if (aoview_log_failed)
+ return;
+ aoview_log_start();
+ va_start(ap, format);
+ vfprintf(aoview_log_file, format, ap);
+ va_end(ap);
+}
+
+void
+aoview_log_init(GladeXML *xml)
+{
+ GConfClient *gconf_client;
+ char *log_dir = NULL;
+ GtkFileChooser *log_chooser_dialog;
+ GtkWidget *log_configure_ok;
+ GtkWidget *log_new;
+
+ g_type_init();
+ gconf_client = gconf_client_get_default();
+ if (gconf_client)
+ {
+ log_dir = gconf_client_get_string(gconf_client,
+ LOG_DIR_PATH,
+ NULL);
+ g_object_unref(G_OBJECT(gconf_client));
+ }
+ if (!log_dir) {
+ aoview_log_dir = aoview_fullname(getenv("HOME"), DEFAULT_LOG);
+ aoview_log_save_conf();
+ } else {
+ aoview_log_dir = strdup(log_dir);
+ }
+
+ log_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "log_chooser_dialog"));
+ assert(log_chooser_dialog);
+ gtk_file_chooser_set_filename(log_chooser_dialog, aoview_log_dir);
+
+ log_configure_ok = glade_xml_get_widget(xml, "log_configure_ok");
+ assert(log_configure_ok);
+
+ g_signal_connect(G_OBJECT(log_configure_ok), "clicked",
+ G_CALLBACK(aoview_log_configure),
+ log_chooser_dialog);
+
+ log_new = glade_xml_get_widget(xml, "ao_log_new");
+ assert(log_new);
+ g_signal_connect(G_OBJECT(log_new), "activate",
+ G_CALLBACK(aoview_log_new_item),
+ NULL);
+
+ log_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "log_fail_dialog"));
+ assert(log_fail_dialog);
+}
aoview_state_init(xml);
+ aoview_log_init(xml);
+
+ aoview_table_init(xml);
+
gtk_main();
return 0;
int nword;
struct aostate state;
- printf ("%s\n", line);
+ if (aoview_log_get_serial())
+ aoview_log_printf ("%s\n", line);
for (nword = 0; nword < 64; nword++) {
words[nword] = strtok_r(line, " \t\n", &saveptr);
line = NULL;
return;
aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]);
aoview_parse_int(&state.serial, words[3]);
+ if (!aoview_log_get_serial())
+ aoview_log_set_serial(state.serial);
+
aoview_parse_int(&state.rssi, words[5]);
aoview_parse_string(state.state, sizeof (state.state), words[9]);
aoview_parse_int(&state.tick, words[10]);
aoview_parse_int(&state.batt, words[18]);
aoview_parse_int(&state.drogue, words[20]);
aoview_parse_int(&state.main, words[22]);
+ aoview_parse_int(&state.nsat, words[24]);
if (strcmp (words[26], "unlocked") != 0 && nword >= 29) {
+ state.locked = 1;
sscanf(words[26], "%d:%d:%d", &state.gps_time.hour, &state.gps_time.minute, &state.gps_time.second);
aoview_parse_pos(&state.lat, words[27]);
aoview_parse_pos(&state.lon, words[28]);
sscanf(words[29], "%dm", &state.alt);
} else {
+ state.locked = 0;
state.gps_time.hour = state.gps_time.minute = state.gps_time.second = 0;
state.lat = state.lon = 0;
state.alt = 0;
int ticks;
double dist;
double bearing;
+ double temp;
+ double battery;
+ double drogue_sense, main_sense;
if (!strcmp(state->state, "pad")) {
if (npad < NUM_PAD_SAMPLES) {
velocity_change = (accel + prev_accel) / 2.0;
ticks = state->tick - prev_tick;
velocity -= velocity_change * (ticks / 100.0);
+ temp = ((state->temp / 32767.0 * 3.3) - 0.5) / 0.01;
+ battery = (state->batt / 32767.0 * 5.0);
+ drogue_sense = (state->drogue / 32767.0 * 15.0);
+ main_sense = (state->main / 32767.0 * 15.0);
prev_accel = accel;
prev_tick = state->tick;
- printf ("Pad altitude: %dm\n", aoview_pres_to_altitude(pad_pres));
- printf ("AGL: %dm\n", altitude);
- printf ("Acceleration: %gm/s²\n", accel);
- printf ("Velocity: %gm/s\n", velocity);
- printf ("Lat: %g\n", state->lat);
- printf ("Lon: %g\n", state->lon);
- printf ("GPS alt: %d\n", state->alt);
- aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon,
- &dist, &bearing);
- printf ("Course: %gkm %g°\n", dist, bearing);
+ aoview_table_start();
+ aoview_table_add_row("RSSI", "%ddB", state->rssi);
+ aoview_table_add_row("Height", "%dm", altitude);
+ aoview_table_add_row("Acceleration", "%gm/s²", accel);
+ aoview_table_add_row("Velocity", "%gm/s", velocity);
+ aoview_table_add_row("Temperature", "%g°C", temp);
+ aoview_table_add_row("Battery", "%gV", battery);
+ aoview_table_add_row("Drogue", "%gV", drogue_sense);
+ aoview_table_add_row("Main", "%gV", main_sense);
+ aoview_table_add_row("Pad altitude", "%dm", aoview_pres_to_altitude(pad_pres));
+ aoview_table_add_row("Satellites", "%d", state->nsat);
+ if (state->locked) {
+ aoview_table_add_row("Lat", "%g", state->lat);
+ aoview_table_add_row("Lon", "%g", state->lon);
+ aoview_table_add_row("GPS alt", "%d", state->alt);
+ aoview_table_add_row("GPS time", "%02d:%02d:%02d",
+ state->gps_time.hour,
+ state->gps_time.minute,
+ state->gps_time.second);
+ aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon,
+ &dist, &bearing);
+ aoview_table_add_row("Course", "%gkm %g°", dist, bearing);
+ } else {
+ aoview_table_add_row("GPS", "unlocked");
+ }
+ aoview_table_finish();
}
void
--- /dev/null
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static GtkTreeView *dataview;
+static GtkListStore *datalist;
+
+void
+aoview_table_start(void)
+{
+ datalist = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+void
+aoview_table_add_row(char *label, char *format, ...)
+{
+ char buf[1024];
+ va_list ap;
+ GtkTreeIter iter;
+
+ va_start(ap, format);
+ vsnprintf(buf, sizeof (buf), format, ap);
+ va_end(ap);
+ gtk_list_store_append(datalist, &iter);
+ gtk_list_store_set(datalist, &iter,
+ 0, label,
+ 1, buf,
+ -1);
+}
+
+void
+aoview_table_finish(void)
+{
+ gtk_tree_view_set_model(dataview, GTK_TREE_MODEL(datalist));
+ g_object_unref(G_OBJECT(datalist));
+ gtk_tree_view_columns_autosize(dataview);
+}
+
+void
+aoview_table_init(GladeXML *xml)
+{
+ dataview = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dataview"));
+ assert(dataview);
+
+ aoview_add_plain_text_column(dataview, "Field", 0, 16);
+ aoview_add_plain_text_column(dataview, "Value", 1, 32);
+}
--- /dev/null
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+char *
+aoview_fullname (char *dir, char *file)
+{
+ char *new;
+ int dlen = strlen (dir);
+ int flen = strlen (file);
+ int slen = 0;
+
+ if (dir[dlen-1] != '/')
+ slen = 1;
+ new = malloc (dlen + slen + flen + 1);
+ if (!new)
+ return 0;
+ strcpy(new, dir);
+ if (slen)
+ strcat (new, "/");
+ strcat(new, file);
+ return new;
+}
+
+char *
+aoview_basename(char *file)
+{
+ char *b;
+
+ b = strrchr(file, '/');
+ if (!b)
+ return file;
+ return b + 1;
+}
+
+int
+aoview_mkdir(char *dir)
+{
+ char *slash;
+ char *d;
+ char *part;
+
+ d = dir;
+ for (;;) {
+ slash = strchr (d, '/');
+ if (!slash)
+ slash = d + strlen(d);
+ if (!*slash)
+ break;
+ part = strndup(dir, slash - dir);
+ if (!access(part, F_OK))
+ if (mkdir(part, 0777) < 0)
+ return -errno;
+ free(part);
+ d = slash + 1;
+ }
+ return 0;
+}
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
+ g_object_set(renderer, "width-chars", width, NULL);
+ column = gtk_tree_view_column_new_with_attributes (title, renderer,
+ "text", model_column,
+ NULL);
+ gtk_tree_view_column_set_resizable (column, FALSE);
+ gtk_tree_view_append_column (view, column);
+
+ return column;
+}