From 09771c644de54ae354e8f98af7ba74289b3c0fcc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 16 May 2009 02:25:04 -0700 Subject: [PATCH] Add preliminary aoview code AoView connects with TeleDongle to present telemetry information in a reasonable form. Right now, it just displays information to stdout, but it does have fancy dialogs for finding the USB devices. Signed-off-by: Keith Packard --- aoview/Makefile | 30 +++ aoview/aoview.glade | 429 +++++++++++++++++++++++++++++++++++++ aoview/aoview.h | 118 ++++++++++ aoview/aoview_convert.c | 42 ++++ aoview/aoview_dev.c | 213 ++++++++++++++++++ aoview/aoview_dev_dialog.c | 146 +++++++++++++ aoview/aoview_main.c | 75 +++++++ aoview/aoview_monitor.c | 144 +++++++++++++ aoview/aoview_serial.c | 270 +++++++++++++++++++++++ aoview/aoview_state.c | 111 ++++++++++ aoview/design | 27 +++ 11 files changed, 1605 insertions(+) create mode 100644 aoview/Makefile create mode 100644 aoview/aoview.glade create mode 100644 aoview/aoview.h create mode 100644 aoview/aoview_convert.c create mode 100644 aoview/aoview_dev.c create mode 100644 aoview/aoview_dev_dialog.c create mode 100644 aoview/aoview_main.c create mode 100644 aoview/aoview_monitor.c create mode 100644 aoview/aoview_serial.c create mode 100644 aoview/aoview_state.c create mode 100644 aoview/design diff --git a/aoview/Makefile b/aoview/Makefile new file mode 100644 index 00000000..bdf7853c --- /dev/null +++ b/aoview/Makefile @@ -0,0 +1,30 @@ +MODULES=gtk+-2.0 libglade-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) +LIBS=$(shell pkg-config --libs $(MODULES)) -lm + +SRC = \ + aoview_main.c \ + aoview_dev.c \ + aoview_dev_dialog.c \ + aoview_serial.c \ + aoview_monitor.c \ + aoview_state.c \ + aoview_convert.c + +INC = \ + aoview.h + +OBJ = \ + $(SRC:.c=.o) + +PROG = aoview + +$(PROG): $(OBJ) + $(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS) + +$(OBJ): $(INC) + +clean: + rm -f $(OBJ) $(PROG) diff --git a/aoview/aoview.glade b/aoview/aoview.glade new file mode 100644 index 00000000..e60f17f7 --- /dev/null +++ b/aoview/aoview.glade @@ -0,0 +1,429 @@ + + + + + + 300 + 300 + True + AltOS View + + + True + vertical + + + True + + + True + _File + True + + + True + + + gtk-new + True + True + True + + + + + gtk-open + True + True + True + + + + + gtk-save + True + True + True + + + + + gtk-save-as + True + True + True + + + + + True + + + + + gtk-quit + True + True + True + + + + + + + + + + True + _Edit + True + + + True + + + gtk-cut + True + True + True + + + + + gtk-copy + True + True + True + + + + + gtk-paste + True + True + True + + + + + gtk-delete + True + True + True + + + + + + + + + True + _Device + True + + + True + + + _Connect to device + True + True + False + + + + + True + gtk-connect + + + + + + + _Disconnect + True + True + False + + + True + gtk-disconnect + + + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + 7 + 2 + + + True + Altitude + + + + + True + Velocity + + + 1 + 2 + + + + + True + Acceleration + + + 2 + 3 + + + + + True + Latitude + + + 3 + 4 + + + + + True + Longitude + + + 4 + 5 + + + + + True + Range + + + 5 + 6 + + + + + True + Direction + + + 6 + 7 + + + + + True + 0m + True + True + + + 1 + 2 + + + + + True + 150m/s + True + + + 1 + 2 + 1 + 2 + + + + + True + 50m/s² + True + + + 1 + 2 + 2 + 3 + + + + + True + 45° 31' 24'' + True + + + 1 + 2 + 3 + 4 + + + + + True + 122° 40' 34''W + True + + + 1 + 2 + 4 + 5 + + + + + True + 500m + True + + + 1 + 2 + 5 + 6 + + + + + True + 45° + True + + + 1 + 2 + 6 + 7 + + + + + 1 + + + + + + + 5 + normal + False + + + True + vertical + 2 + + + 300 + 100 + True + True + False + True + 0 + False + 1 + both + True + + + 1 + + + + + True + end + + + gtk-connect + True + True + True + True + True + + + False + False + 0 + + + + + gtk-cancel + 1 + True + True + True + True + True + + + + False + False + 1 + + + + + False + end + 0 + + + + + + diff --git a/aoview/aoview.h b/aoview/aoview.h new file mode 100644 index 00000000..028b2f16 --- /dev/null +++ b/aoview/aoview.h @@ -0,0 +1,118 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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. + */ + +#ifndef _AOVIEW_H_ +#define _AOVIEW_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usbdev { + char *sys; + char *tty; + char *manufacturer; + char *product; + char *serial; + int idProduct; + int idVendor; +}; + +struct aostate { + char callsign[16]; + int serial; + int rssi; + char state[16]; + int tick; + int accel; + int pres; + int temp; + int batt; + int drogue; + int main; + int nsat; + int locked; + struct { + int hour; + int minute; + int second; + } gps_time; + double lat; + double lon; + int alt; +}; + +void +aoview_monitor_disconnect(void); + +void +aoview_monitor_connect(char *tty); + +struct aoview_serial * +aoview_serial_open(const char *tty); + +void +aoview_serial_close(struct aoview_serial *serial); + +void +aoview_serial_set_callback(struct aoview_serial *serial, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); + +void +aoview_serial_printf(struct aoview_serial *serial, char *format, ...); + +int +aoview_serial_read(struct aoview_serial *serial, char *buf, int len); + +int +aoview_serial_getc(struct aoview_serial *serial); + +void +aoview_dev_dialog_init(GladeXML *xml); + +int +aoview_usb_scan(struct usbdev ***devs_ret); + +void +aoview_usbdev_free(struct usbdev *usbdev); + +void +aoview_state_notify(struct aostate *state); + +void +aoview_state_init(GladeXML *xml); + +int16_t +aoview_pres_to_altitude(int16_t pres); + +int16_t +aoview_altitude_to_pres(int16_t alt); + +#endif /* _AOVIEW_H_ */ diff --git a/aoview/aoview_convert.c b/aoview/aoview_convert.c new file mode 100644 index 00000000..a4bf813d --- /dev/null +++ b/aoview/aoview_convert.c @@ -0,0 +1,42 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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 int16_t altitude_table[2048] = { +#include "../altitude.h" +}; + +int16_t +aoview_pres_to_altitude(int16_t pres) +{ + pres = pres >> 4; + if (pres < 0) pres = 0; + if (pres > 2047) pres = 2047; + return altitude_table[pres]; +} + +int16_t +aoview_altitude_to_pres(int16_t alt) +{ + int16_t pres; + + for (pres = 0; pres < 2047; pres++) + if (altitude_table[pres] <= alt) + break; + return pres << 4; +} diff --git a/aoview/aoview_dev.c b/aoview/aoview_dev.c new file mode 100644 index 00000000..5bb3cc9f --- /dev/null +++ b/aoview/aoview_dev.c @@ -0,0 +1,213 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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" +#include +#include + +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 line[4096]; + char *r; + FILE *f; + int rlen; + + f = fopen(full, "r"); + free(full); + if (!f) + return NULL; + r = fgets(line, sizeof (line), f); + fclose(f); + if (!r) + return NULL; + rlen = strlen(r); + if (r[rlen-1] == '\n') + r[rlen-1] = '\0'; + return strdup(r); +} + +static int +load_hex(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 16); + free(line); + if (end == line) + return -1; + 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) +{ + return strncmp(d->d_name, "tty:", 4) == 0; +} + +static char * +usb_tty(char *sys) +{ + char *base; + int num_configs; + int config; + struct dirent **namelist; + int interface; + int num_interfaces; + char endpoint_base[20]; + char *endpoint_full; + int ntty; + char *tty; + + base = 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); + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty, + alphasort); + free(endpoint_full); + if (ntty) { + tty = fullname("/dev", namelist[0]->d_name + 4); + free(namelist); + return tty; + } + } + } + return NULL; +} + +static struct usbdev * +usb_scan_device(char *sys) +{ + struct usbdev *usbdev; + + usbdev = calloc(1, sizeof (struct usbdev)); + if (!usbdev) + return NULL; + usbdev->sys = strdup(sys); + usbdev->manufacturer = load_string(sys, "manufacturer"); + usbdev->product = load_string(sys, "product"); + usbdev->serial = load_string(sys, "serial"); + usbdev->idProduct = load_hex(sys, "idProduct"); + usbdev->idVendor = load_hex(sys, "idVendor"); + usbdev->tty = usb_tty(sys); + return usbdev; +} + +void +aoview_usbdev_free(struct usbdev *usbdev) +{ + free(usbdev->sys); + free(usbdev->manufacturer); + free(usbdev->product); + free(usbdev->serial); + free(usbdev->tty); + free(usbdev); +} + +#define USB_DEVICES "/sys/bus/usb/devices" + +static int +dir_filter_dev(const struct dirent *d) +{ + const char *n = d->d_name; + char c; + + while ((c = *n++)) { + if (isdigit(c)) + continue; + if (c == '-') + continue; + return 0; + } + return 1; +} + +int +aoview_usb_scan(struct usbdev ***devs_ret) +{ + int n; + int ndev = 0; + int e; + struct dirent **ents; + char *dir; + struct usbdev **devs = NULL; + struct usbdev *dev; + + n = scandir (USB_DEVICES, &ents, + dir_filter_dev, + alphasort); + if (!n) + return 0; + for (e = 0; e < n; e++) { + dir = fullname(USB_DEVICES, ents[e]->d_name); + dev = usb_scan_device(dir); + free(dir); + if (dev->idVendor == 0xfffe && dev->tty) { + if (devs) + devs = realloc(devs, ndev + 1 * sizeof (struct usbdev *)); + else + devs = malloc (sizeof (struct usbdev *)); + devs[ndev++] = dev; + } + } + free(ents); + *devs_ret = devs; + return ndev; +} diff --git a/aoview/aoview_dev_dialog.c b/aoview/aoview_dev_dialog.c new file mode 100644 index 00000000..f1165e12 --- /dev/null +++ b/aoview/aoview_dev_dialog.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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 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) +{ + GtkTreeView *dev_list = data; + GtkListStore *list_store; + GtkTreeIter iter; + int ndev, n; + struct usbdev **devs; + + list_store = gtk_list_store_new(3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + + ndev = aoview_usb_scan(&devs); + for (n = 0; n < ndev; n++) { + gtk_list_store_append(list_store, &iter); + gtk_list_store_set(list_store, &iter, + 0, devs[n]->product, + 1, devs[n]->serial, + 2, devs[n]->tty, + -1); + } + gtk_tree_view_set_model (dev_list, GTK_TREE_MODEL(list_store)); + g_object_unref(G_OBJECT(list_store)); + gtk_tree_view_columns_autosize(dev_list); +} + +static void +aoview_dev_selected(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + gchar *string; + gtk_tree_model_get(model, iter, + 2, &string, + -1); + aoview_monitor_connect(string); +} + +static GtkWidget *dialog; + +static void +aoview_dev_dialog_connect(GtkWidget *widget, gpointer data) +{ + GtkTreeView *dev_list = data; + GtkListStore *list_store; + GtkTreeSelection *tree_selection; + + list_store = GTK_LIST_STORE(gtk_tree_view_get_model(dev_list)); + tree_selection = gtk_tree_view_get_selection(dev_list); + gtk_tree_selection_selected_foreach(tree_selection, + aoview_dev_selected, + data); + + gtk_widget_hide(dialog); +} + +static void +aoview_dev_disconnect(GtkWidget *widget) +{ + aoview_monitor_disconnect(); +} + +#define _(a) a + +void +aoview_dev_dialog_init(GladeXML *xml) +{ + GtkTreeView *dev_list; + GtkWidget *connect_button; + GtkTreeSelection *dev_selection; + GtkWidget *ao_disconnect; + + dialog = glade_xml_get_widget(xml, "device_connect_dialog"); + assert(dialog); + + 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); + + dev_selection = gtk_tree_view_get_selection(dev_list); + gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE); + + g_signal_connect(G_OBJECT(dialog), "map", + G_CALLBACK(aoview_dev_dialog_map), + dev_list); + + connect_button = glade_xml_get_widget(xml, "connect_button"); + assert(connect_button); + + g_signal_connect(G_OBJECT(connect_button), "clicked", + G_CALLBACK(aoview_dev_dialog_connect), + dev_list); + + + ao_disconnect = glade_xml_get_widget(xml, "ao_disconnect"); + assert(ao_disconnect); + + g_signal_connect(G_OBJECT(ao_disconnect), "activate", + G_CALLBACK(aoview_dev_disconnect), + ao_disconnect); +} diff --git a/aoview/aoview_main.c b/aoview/aoview_main.c new file mode 100644 index 00000000..7906e8ac --- /dev/null +++ b/aoview/aoview_main.c @@ -0,0 +1,75 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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 void usage(void) { + printf("aoview [--device|-d device_file]"); + exit(1); +} + +static void destroy_event(GtkWidget *widget, gpointer data) +{ + gtk_main_quit(); +} + +int main(int argc, char **argv) +{ + GladeXML *xml = NULL; + GtkWidget *mainwindow; + char *device = NULL; + + static struct option long_options[] = { + { "device", 1, 0, 'd'}, + { 0, 0, 0, 0 } + }; + for (;;) { + int c, temp; + + c = getopt_long_only(argc, argv, "d:", long_options, &temp); + if (c == -1) + break; + + switch (c) { + case 'd': + device = optarg; + break; + default: + usage(); + } + } + + gtk_init(&argc, &argv); + glade_init(); + + xml = glade_xml_new("aoview.glade", NULL, NULL); + /* connect the signals in the interface */ + glade_xml_signal_autoconnect(xml); + + /* Hook up the close button. */ + mainwindow = glade_xml_get_widget(xml, "aoview"); + g_signal_connect (G_OBJECT(mainwindow), "destroy", + G_CALLBACK(destroy_event), NULL); + + aoview_dev_dialog_init(xml); + + aoview_state_init(xml); + + gtk_main(); + + return 0; +} diff --git a/aoview/aoview_monitor.c b/aoview/aoview_monitor.c new file mode 100644 index 00000000..1c9b7189 --- /dev/null +++ b/aoview/aoview_monitor.c @@ -0,0 +1,144 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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 struct aoview_serial *monitor_serial; + +#define MONITOR_LEN 1024 + +static char monitor_line[MONITOR_LEN + 1]; +static int monitor_pos; + +void +aoview_monitor_disconnect(void) +{ + if (monitor_serial) { + aoview_serial_close(monitor_serial); + monitor_serial = NULL; + } +} + +static void +aoview_parse_string(char *target, int len, char *source) +{ + strncpy(target, source, len-1); + target[len-1] = '\0'; +} + +static void +aoview_parse_int(int *target, char *source) +{ + *target = strtol(source, NULL, 0); +} + +static void +aoview_parse_pos(double *target, char *source) +{ + int deg; + double min; + char dir; + double r; + + if (sscanf(source, "%d°%lf'%c", °, &min, &dir) != 3) { + *target = 0; + return; + } + r = deg + min / 60.0; + if (dir == 'S' || dir == 'W') + r = -r; + *target = r; +} + +static void +aoview_monitor_parse(char *line) +{ + char *saveptr; + char *words[64]; + int nword; + struct aostate state; + + printf ("%s\n", line); + for (nword = 0; nword < 64; nword++) { + words[nword] = strtok_r(line, " \t\n", &saveptr); + line = NULL; + if (words[nword] == NULL) + break; + } + if (nword < 26) + return; + if (strcmp(words[0], "CALL") != 0) + return; + aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]); + aoview_parse_int(&state.serial, words[3]); + 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.accel, words[12]); + aoview_parse_int(&state.pres, words[14]); + aoview_parse_int(&state.temp, words[16]); + aoview_parse_int(&state.batt, words[18]); + aoview_parse_int(&state.drogue, words[20]); + aoview_parse_int(&state.main, words[22]); + if (strcmp (words[26], "unlocked") != 0 && nword >= 29) { + 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.gps_time.hour = state.gps_time.minute = state.gps_time.second = 0; + state.lat = state.lon = 0; + state.alt = 0; + } + aoview_state_notify(&state); +} + +static gboolean +aoview_monitor_callback(void *user_data) +{ + int c; + + if (!monitor_serial) + return FALSE; + + for (;;) { + c = aoview_serial_getc(monitor_serial); + if (c == -1) + break; + if (c == '\r') + continue; + if (c == '\n') { + monitor_line[monitor_pos] = '\0'; + if (monitor_pos) + aoview_monitor_parse(monitor_line); + monitor_pos = 0; + } else if (monitor_pos < MONITOR_LEN) + monitor_line[monitor_pos++] = c; + } + return TRUE; +} + +void +aoview_monitor_connect(char *tty) +{ + aoview_monitor_disconnect(); + monitor_serial = aoview_serial_open(tty); + aoview_serial_set_callback(monitor_serial, + aoview_monitor_callback, + monitor_serial, + NULL); +} diff --git a/aoview/aoview_serial.c b/aoview/aoview_serial.c new file mode 100644 index 00000000..5cb286f8 --- /dev/null +++ b/aoview/aoview_serial.c @@ -0,0 +1,270 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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" +#include + +#define AOVIEW_SERIAL_IN_BUF 64 +#define AOVIEW_SERIAL_OUT_BUF 64 + +struct aoview_buf { + char *buf; + int off; + int count; + int size; +}; + +static int +aoview_buf_write(struct aoview_buf *buf, char *data, int len) +{ + if (buf->count + len > buf->size) { + int new_size = buf->size * 2; + if (new_size == 0) + new_size = 1024; + if (buf->buf) + buf->buf = realloc (buf->buf, new_size); + else + buf->buf = malloc (new_size); + buf->size = new_size; + } + memcpy(buf->buf + buf->count, data, len); + buf->count += len; + return len; +} + +static int +aoview_buf_read(struct aoview_buf *buf, char *data, int len) +{ + if (len > buf->count - buf->off) + len = buf->count - buf->off; + memcpy (data, buf->buf + buf->off, len); + buf->off += len; + if (buf->off == buf->count) + buf->off = buf->count = 0; + return len; +} + +static int +aoview_buf_getc(struct aoview_buf *buf) +{ + char b; + int r; + + r = aoview_buf_read(buf, &b, 1); + if (r == 1) + return (int) b; + return -1; +} + +static void +aoview_buf_flush(struct aoview_buf *buf, int fd) +{ + int ret; + + if (buf->count > buf->off) { + ret = write(fd, buf->buf + buf->off, buf->count - buf->off); + if (ret > 0) { + buf->off += ret; + if (buf->off == buf->count) + buf->off = buf->count = 0; + } + } +} + +static void +aoview_buf_fill(struct aoview_buf *buf, int fd) +{ + int ret; + + while (buf->count >= buf->size) { + int new_size = buf->size * 2; + buf->buf = realloc (buf->buf, new_size); + buf->size = new_size; + } + + ret = read(fd, buf->buf + buf->count, buf->size - buf->count); + if (ret > 0) + buf->count += ret; +} + +static void +aoview_buf_init(struct aoview_buf *buf) +{ + buf->buf = malloc (buf->size = 1024); + buf->count = 0; +} + +static void +aoview_buf_fini(struct aoview_buf *buf) +{ + free(buf->buf); +} + +struct aoview_serial { + GSource source; + int fd; + struct termios save_termios; + struct aoview_buf in_buf; + struct aoview_buf out_buf; + GPollFD poll_fd; +}; + + +void +aoview_serial_printf(struct aoview_serial *serial, char *format, ...) +{ + char buf[1024]; + va_list ap; + int ret; + + /* sprintf to a local buffer */ + va_start(ap, format); + ret = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + if (ret > sizeof(buf)) { + fprintf(stderr, "printf overflow for format %s\n", + format); + } + + /* flush local buffer to the wire */ + aoview_buf_write(&serial->out_buf, buf, ret); + aoview_buf_flush(&serial->out_buf, serial->fd); +} + +int +aoview_serial_read(struct aoview_serial *serial, char *buf, int len) +{ + return aoview_buf_read(&serial->in_buf, buf, len); +} + +int +aoview_serial_getc(struct aoview_serial *serial) +{ + return aoview_buf_getc(&serial->in_buf); +} + +static gboolean +serial_prepare(GSource *source, gint *timeout) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + *timeout = -1; + + if (serial->out_buf.count) + serial->poll_fd.events |= G_IO_OUT; + else + serial->poll_fd.events &= ~G_IO_OUT; + return FALSE; +} + +static gboolean +serial_check(GSource *source) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + gint revents = serial->poll_fd.revents; + + if (revents & G_IO_NVAL) + return FALSE; + if (revents & G_IO_IN) + return TRUE; + if (revents & G_IO_OUT) + return TRUE; + return FALSE; +} + +static gboolean +serial_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + gint revents = serial->poll_fd.revents; + + if (revents & G_IO_IN) + aoview_buf_fill(&serial->in_buf, serial->fd); + + if (revents & G_IO_OUT) + aoview_buf_flush(&serial->out_buf, serial->fd); + + if (callback && (revents & G_IO_IN)) + (*callback)(user_data); + return TRUE; +} + +static void +serial_finalize(GSource *source) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + + aoview_buf_fini(&serial->in_buf); + aoview_buf_fini(&serial->out_buf); + tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios); + close (serial->fd); +} + +static GSourceFuncs serial_funcs = { + serial_prepare, + serial_check, + serial_dispatch, + serial_finalize +}; + +struct aoview_serial * +aoview_serial_open(const char *tty) +{ + struct aoview_serial *serial; + struct termios termios; + + serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial)); + aoview_buf_init(&serial->in_buf); + aoview_buf_init(&serial->out_buf); + serial->fd = open (tty, O_RDWR | O_NONBLOCK); + if (serial->fd < 0) { + free (serial); + return NULL; + } + tcgetattr(serial->fd, &termios); + serial->save_termios = termios; + cfmakeraw(&termios); + tcsetattr(serial->fd, TCSAFLUSH, &termios); + + aoview_serial_printf(serial, "E 0\n"); + tcdrain(serial->fd); + usleep(15*1000); + tcflush(serial->fd, TCIFLUSH); + serial->poll_fd.fd = serial->fd; + serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; + g_source_attach(&serial->source, NULL); + g_source_add_poll(&serial->source,&serial->poll_fd); + return serial; +} + +void +aoview_serial_close(struct aoview_serial *serial) +{ + g_source_remove_poll(&serial->source, &serial->poll_fd); + g_source_destroy(&serial->source); + g_source_unref(&serial->source); +} + +void +aoview_serial_set_callback(struct aoview_serial *serial, + GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + g_source_set_callback(&serial->source, func, data, notify); +} diff --git a/aoview/aoview_state.c b/aoview/aoview_state.c new file mode 100644 index 00000000..efd49042 --- /dev/null +++ b/aoview/aoview_state.c @@ -0,0 +1,111 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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" +#include + +static int pad_pres; +static int pad_accel; + +static int pad_pres_total; +static int pad_accel_total; +static double pad_lat_total; +static double pad_lon_total; +static int pad_alt_total; +static int npad; +static int prev_tick; +static double prev_accel; +static double velocity; +static double pad_lat; +static double pad_lon; +static double pad_alt; + +#define NUM_PAD_SAMPLES 50 + + + +static void +aoview_great_circle (double start_lat, double start_lon, + double end_lat, double end_lon, + double *dist, double *bearing) +{ + double rad = M_PI / 180; + double earth_radius = 6371.2; + double a = (90 - start_lat) * rad; + double b = (90 - end_lat) * rad; + double phi = (end_lon - start_lon) * rad; + double cosr = cos(a) * cos(b) + sin(a) * sin(b) * cos(phi); + double r = acos(cosr); + double rdist = earth_radius * r; + double sinth = sin(phi) * sin(b) / sin(r); + double th = asin(sinth) / rad; + *dist = rdist; + *bearing = th; +} + +void +aoview_state_notify(struct aostate *state) +{ + int altitude; + double accel; + double velocity_change; + int ticks; + double dist; + double bearing; + + if (!strcmp(state->state, "pad")) { + if (npad < NUM_PAD_SAMPLES) { + pad_accel_total += state->accel; + pad_pres_total += state->pres; + pad_lat_total += state->lat; + pad_lon_total += state->lon; + pad_alt_total += state->alt; + npad++; + velocity = 0; + } + if (npad <= NUM_PAD_SAMPLES) { + pad_pres = pad_pres_total / npad; + pad_accel = pad_accel_total / npad; + pad_lat = pad_lat_total / npad; + pad_lon = pad_lon_total / npad; + pad_alt = pad_alt_total / npad; + } + } + altitude = aoview_pres_to_altitude(state->pres) - aoview_pres_to_altitude(pad_pres); + accel = (pad_accel - state->accel) / 264.8 * 9.80665; + velocity_change = (accel + prev_accel) / 2.0; + ticks = state->tick - prev_tick; + velocity -= velocity_change * (ticks / 100.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); +} + +void +aoview_state_init(GladeXML *xml) +{ +} diff --git a/aoview/design b/aoview/design new file mode 100644 index 00000000..6ec2ea70 --- /dev/null +++ b/aoview/design @@ -0,0 +1,27 @@ +Requirements: + real-time display of telemetry + off-line display of logged data + Logging of telemetry + Capture of logged data to disk + +Input data: + accelerometer + barometer + thermometer + gps + drogue and main continuity + battery voltage + time + reported flight state + reported events + +Computed data: + velocity (from accelerometer) + altitude + range + direction + +Displays: + numeric display of current rocket status + (graphics come later) + text message log -- 2.30.2