Add preliminary aoview code sn4-flight1 sn4-flight2
authorKeith Packard <keithp@keithp.com>
Sat, 16 May 2009 09:25:04 +0000 (02:25 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 16 May 2009 09:25:04 +0000 (02:25 -0700)
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 <keithp@keithp.com>
aoview/Makefile [new file with mode: 0644]
aoview/aoview.glade [new file with mode: 0644]
aoview/aoview.h [new file with mode: 0644]
aoview/aoview_convert.c [new file with mode: 0644]
aoview/aoview_dev.c [new file with mode: 0644]
aoview/aoview_dev_dialog.c [new file with mode: 0644]
aoview/aoview_main.c [new file with mode: 0644]
aoview/aoview_monitor.c [new file with mode: 0644]
aoview/aoview_serial.c [new file with mode: 0644]
aoview/aoview_state.c [new file with mode: 0644]
aoview/design [new file with mode: 0644]

diff --git a/aoview/Makefile b/aoview/Makefile
new file mode 100644 (file)
index 0000000..bdf7853
--- /dev/null
@@ -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 (file)
index 0000000..e60f17f
--- /dev/null
@@ -0,0 +1,429 @@
+<?xml version="1.0"?>
+<glade-interface>
+  <!-- interface-requires gtk+ 2.16 -->
+  <!-- interface-naming-policy project-wide -->
+  <widget class="GtkWindow" id="aoview">
+    <property name="width_request">300</property>
+    <property name="height_request">300</property>
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">AltOS View</property>
+    <child>
+      <widget class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <widget class="GtkMenuBar" id="menubar1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_File</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu1">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem1">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem2">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem3">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem4">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem5">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="gtk_main_quit"/>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Edit</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu2">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem6">
+                        <property name="label">gtk-cut</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem8">
+                        <property name="label">gtk-paste</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem9">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem3">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Device</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu4">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_connect">
+                        <property name="label" translatable="yes">_Connect to device</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate_item" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
+                        <signal name="activate" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image1">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-connect</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_disconnect">
+                        <property name="label" translatable="yes">_Disconnect</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="image2">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-disconnect</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Help</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu3">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem10">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkTable" id="table1">
+            <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&#xB2;</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&#xB0; 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&#xB0; 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&#xB0;</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>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="device_connect_dialog">
+    <property name="border_width">5</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkTreeView" id="dev_list">
+            <property name="width_request">300</property>
+            <property name="height_request">100</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="headers_clickable">False</property>
+            <property name="rules_hint">True</property>
+            <property name="search_column">0</property>
+            <property name="show_expanders">False</property>
+            <property name="level_indentation">1</property>
+            <property name="enable_grid_lines">both</property>
+            <property name="enable_tree_lines">True</property>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <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>
+                <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>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <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>
+                <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>
+</glade-interface>
diff --git a/aoview/aoview.h b/aoview/aoview.h
new file mode 100644 (file)
index 0000000..028b2f1
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#ifndef _AOVIEW_H_
+#define _AOVIEW_H_
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+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 (file)
index 0000000..a4bf813
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 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 (file)
index 0000000..5bb3cc9
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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"
+#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    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 (file)
index 0000000..f1165e1
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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 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 (file)
index 0000000..7906e8a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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 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 (file)
index 0000000..1c9b718
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 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", &deg, &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 (file)
index 0000000..5cb286f
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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"
+#include <termios.h>
+
+#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 (file)
index 0000000..efd4904
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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"
+#include <math.h>
+
+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 (file)
index 0000000..6ec2ea7
--- /dev/null
@@ -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