*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
-2006, 2007, 2008 Free Software Foundation, Inc.
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
- ./configure CC="cc -Ae"
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
./configure CC="cc -nodtk"
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
Specifying the System Type
==========================
where SYSTEM can have one of these forms:
- OS KERNEL-OS
+ OS
+ KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
-SUBDIRS=src ao-view ao-tools ao-utils
+SUBDIRS=src ao-tools ao-utils
EXTRA_DIST = ChangeLog
-SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-load
+SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-load ao-view
--- /dev/null
+*.o
+aoview
+aoview_glade.h
+aoview_flite
--- /dev/null
+VERSION=$(shell git describe)
+
+AO_VIEW_CFLAGS=-I$(top_srcdir)/ao-tools/lib
+AO_VIEW_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+AM_CFLAGS=$(AO_VIEW_CFLAGS) $(GNOME_CFLAGS) $(ALSA_CFLAGS) -I$(top_srcdir)/src -DAOVIEW_VERSION=\"$(VERSION)\" @FLITE_INCS@
+
+bin_PROGRAMS=ao-view
+
+ao_view_DEPENDENCIES=$(AO_VIEW_LIBS)
+ao_view_LDADD=$(GNOME_LIBS) $(FLITE_LIBS) $(ALSA_LIBS) $(AO_VIEW_LIBS) $(LIBUSB_LIBS)
+
+ao_view_SOURCES = \
+ aoview_main.c \
+ aoview_dev_dialog.c \
+ aoview_serial.c \
+ aoview_monitor.c \
+ aoview_state.c \
+ aoview_convert.c \
+ aoview_log.c \
+ aoview_table.c \
+ aoview_util.c \
+ aoview_file.c \
+ aoview_eeprom.c \
+ aoview_voice.c \
+ aoview_replay.c \
+ aoview_label.c \
+ aoview_flite.c \
+ aoview.h
+
+BUILT_SOURCES = aoview_glade.h
+
+CLEANFILES = aoview_glade.h
+
+man_MANS=ao-view.1
+
+aoview_glade.h: aoview.glade
+ sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/"/' $< > $@
--- /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; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" 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.
+.\"
+.\"
+.TH AO-VIEW 1 "ao-view" ""
+.SH NAME
+ao-view \- Rocket flight monitor
+.SH SYNOPSIS
+.B "ao-view"
+[\--tty \fItty-device\fP]
+.SH DESCRIPTION
+.I ao-view
+connects to a TeleDongle or TeleMetrum device through a USB serial device.
+It provides a user interface to monitor, record and review rocket flight data.
+.SH OPTIONS
+The usual Gtk+ command line options can be used, along with
+.IP "\--tty"
+This selects a target device to connect at startup time to.
+The target device may also be selected through the user interface.
+.SH USAGE
+When connected to a TeleDongle device, ao-view turns on the radio
+receiver and listens for telemetry packets. It displays the received
+telemetry data, and reports flight status via voice synthesis. All
+received telemetry information is recorded to a file.
+.P
+When connected to a TeleMetrum device, ao-view downloads the eeprom
+data and stores it in a file.
+.SH FILES
+All data log files are recorded into a user-specified directory
+(default ~/AltOS). Files are named using the current date, the serial
+number of the reporting device, the flight number recorded in the data
+and either '.telem' for telemetry data or '.eeprom' for eeprom data.
+.SH "SEE ALSO"
+ao-load(1), ao-eeprom(1)
+.SH AUTHOR
+Keith Packard
--- /dev/null
+<?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">900</property>
+ <property name="height_request">700</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>
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="seperator">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="ao_savelog">
+ <property name="label" translatable="yes">_Save EEPROM data</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"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="ao_replay">
+ <property name="label" translatable="yes">_Replay</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="ao_replay_dialog" after="yes"/>
+ <signal name="activate" handler="gtk_widget_show" object="ao_replay_dialog"/>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </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="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="file_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="file_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="menuitem6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Voice</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkCheckMenuItem" id="voice_enable">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Enable _Voice</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ </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>
+ <signal name="activate" handler="gtk_widget_show" object="about_dialog" after="yes"/>
+ </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">2</property>
+ <property name="n_columns">4</property>
+ <property name="row_spacing">3</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <widget class="GtkLabel" id="height_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Height (m)</property>
+ <property name="justify">center</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="state_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">State</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="rssi_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">RSSI (dBm)</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="height_value">
+ <property name="visible">True</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">0</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="state_value">
+ <property name="visible">True</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">pad</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="rssi_value">
+ <property name="visible">True</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">-50</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="speed_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Speed (m/s)</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="speed_value">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">0</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkTreeView" id="dataview_0">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_expanders">False</property>
+ <property name="enable_grid_lines">both</property>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTreeView" id="dataview_1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_expanders">False</property>
+ <property name="enable_grid_lines">both</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTreeView" id="dataview_2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_expanders">False</property>
+ <property name="enable_grid_lines">both</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</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="cancel_button">
+ <property name="label">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">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="connect_button">
+ <property name="label">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="file_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="file_configure_cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="gtk_widget_hide" object="file_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="file_configure_ok">
+ <property name="label">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>
+ <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="GtkMessageDialog" id="file_fail_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Failed to create log</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="transient_for">aoview</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>
+ <widget class="GtkMessageDialog" id="dev_open_fail_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Failed to open device</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="transient_for">aoview</property>
+ <property name="message_type">error</property>
+ <property name="buttons">close</property>
+ <property name="text">Cannot open device</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox6">
+ <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_area6">
+ <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>
+ <widget class="GtkAboutDialog" id="about_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">About AoView</property>
+ <property name="resizable">False</property>
+ <property name="type_hint">normal</property>
+ <property name="transient_for">aoview</property>
+ <property name="has_separator">False</property>
+ <property name="program_name">AoView</property>
+ <property name="copyright" translatable="yes">Copyright © 2009 Keith Packard</property>
+ <property name="comments" translatable="yes">AltOS data capture and display.</property>
+ <property name="website">http://altusmetrum.org</property>
+ <property name="license" translatable="yes">AoView 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.
+
+AoView 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 AoView; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property>
+ <property name="authors">Keith Packard <keithp@keithp.com></property>
+ <property name="wrap_license">True</property>
+ <signal name="close" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
+ <signal name="response" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox7">
+ <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_area7">
+ <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>
+ <widget class="GtkMessageDialog" id="ao_save_done">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">EEPROM save complete</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="transient_for">aoview</property>
+ <property name="buttons">close</property>
+ <property name="text">Saving EEPROM data as</property>
+ <property name="secondary_text"><filename></property>
+ <signal name="close" handler="gtk_widget_hide" object="ao_save_done"/>
+ <signal name="response" handler="gtk_widget_hide" object="ao_save_done"/>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox11">
+ <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_area11">
+ <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>
+ <widget class="GtkFileChooserDialog" id="ao_replay_dialog">
+ <property name="border_width">5</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="transient_for">aoview</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox10">
+ <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_area10">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="ao_replay_cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <signal name="clicked" handler="gtk_widget_hide" object="ao_replay_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="ao_replay_ok">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">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>
+</glade-interface>
--- /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.
+ */
+
+#ifndef _AOVIEW_H_
+#define _AOVIEW_H_
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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>
+#include <math.h>
+
+#include "cc.h"
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
+struct aogps_time {
+ int hour;
+ int minute;
+ int second;
+};
+
+struct aogps {
+ int nsat;
+ int gps_locked;
+ int gps_connected;
+ struct aogps_time gps_time;
+ double lat; /* degrees (+N -S) */
+ double lon; /* degrees (+E -W) */
+ int alt; /* m */
+
+ int gps_extended; /* has extra data */
+ double ground_speed; /* m/s */
+ int course; /* degrees */
+ double climb_rate; /* m/s */
+ double hdop; /* unitless? */
+ int h_error; /* m */
+ int v_error; /* m */
+};
+
+#define SIRF_SAT_STATE_ACQUIRED (1 << 0)
+#define SIRF_SAT_STATE_CARRIER_PHASE_VALID (1 << 1)
+#define SIRF_SAT_BIT_SYNC_COMPLETE (1 << 2)
+#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3)
+#define SIRF_SAT_CARRIER_PULLIN_COMPLETE (1 << 4)
+#define SIRF_SAT_CODE_LOCKED (1 << 5)
+#define SIRF_SAT_ACQUISITION_FAILED (1 << 6)
+#define SIRF_SAT_EPHEMERIS_AVAILABLE (1 << 7)
+
+struct aogps_sat {
+ int svid;
+ int state;
+ int c_n0;
+};
+
+struct aogps_tracking {
+ int channels;
+ struct aogps_sat sats[12];
+};
+
+struct aodata {
+ 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 flight_accel;
+ int ground_accel;
+ int flight_vel;
+ int flight_pres;
+ int ground_pres;
+ struct aogps gps;
+ struct aogps_tracking gps_tracking;
+};
+
+struct aostate {
+ struct aodata data;
+
+ /* derived data */
+
+ struct aodata prev_data;
+
+ double report_time;
+
+ gboolean ascent; /* going up? */
+
+ int ground_altitude;
+ int height;
+ double speed;
+ double acceleration;
+ double battery;
+ double temperature;
+ double main_sense;
+ double drogue_sense;
+ double baro_speed;
+
+ int max_height;
+ double max_acceleration;
+ double max_speed;
+
+ struct aogps gps;
+ struct aogps_tracking gps_tracking;
+
+ int gps_valid;
+ double pad_lat;
+ double pad_lon;
+ double pad_alt;
+ double pad_lat_total;
+ double pad_lon_total;
+ double pad_alt_total;
+ int npad;
+ int prev_npad;
+
+ double distance;
+ double bearing;
+ int gps_height;
+
+ int speak_tick;
+ int speak_altitude;
+};
+
+extern struct aostate aostate;
+
+/* GPS is 'stable' when we've seen at least this many samples */
+#define MIN_PAD_SAMPLES 10
+
+void
+aoview_monitor_disconnect(void);
+
+gboolean
+aoview_monitor_connect(char *tty);
+
+gboolean
+aoview_monitor_parse(const char *line);
+
+void
+aoview_monitor_reset(void);
+
+struct aoview_serial *
+aoview_serial_open(const char *tty);
+
+void
+aoview_serial_close(struct aoview_serial *serial);
+
+typedef void (*aoview_serial_callback)(gpointer user_data, struct aoview_serial *serial, gint revents);
+
+void
+aoview_serial_set_callback(struct aoview_serial *serial,
+ aoview_serial_callback func);
+
+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);
+
+void
+aoview_state_notify(struct aodata *data);
+
+void
+aoview_state_new(void);
+
+void
+aoview_state_init(GladeXML *xml);
+
+int16_t
+aoview_pres_to_altitude(int16_t pres);
+
+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_log_new(void);
+
+void
+aoview_table_start(void);
+
+void
+aoview_table_add_row(int column, char *label, char *format, ...);
+
+void
+aoview_table_finish(void);
+
+void
+aoview_table_init(GladeXML *xml);
+
+void
+aoview_table_clear(void);
+
+struct aoview_file;
+
+extern char *aoview_file_dir;
+
+void
+aoview_file_finish(struct aoview_file *file);
+
+gboolean
+aoview_file_start(struct aoview_file *file);
+
+const char *
+aoview_file_name(struct aoview_file *file);
+
+void
+aoview_file_set_serial(struct aoview_file *file, int serial);
+
+int
+aoview_file_get_serial(struct aoview_file *file);
+
+void
+aoview_file_printf(struct aoview_file *file, char *format, ...);
+
+void
+aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap);
+
+struct aoview_file *
+aoview_file_new(char *ext);
+
+void
+aoview_file_destroy(struct aoview_file *file);
+
+void
+aoview_file_init(GladeXML *xml);
+
+/* aoview_eeprom.c */
+
+gboolean
+aoview_eeprom_save(const char *device);
+
+void
+aoview_eeprom_init(GladeXML *xml);
+
+/* aoview_voice.c */
+void aoview_voice_open(void);
+
+void aoview_voice_close(void);
+
+void aoview_voice_speak(char *format, ...);
+
+/* aoview_label.c */
+
+void aoview_label_init(GladeXML *xml);
+
+void
+aoview_label_show(struct aostate *state);
+
+/* aoview_flite.c */
+
+FILE *
+aoview_flite_start(void);
+
+void
+aoview_flite_stop(void);
+
+/* aoview_main.c */
+
+extern char *aoview_tty;
+
+#endif /* _AOVIEW_H_ */
--- /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 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;
+}
--- /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 void
+aoview_dev_dialog_map(GtkWidget *widget, gpointer data)
+{
+ GtkTreeView *dev_list = data;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ int ndev, n;
+ struct cc_usbdevs *devs;
+ struct cc_usbdev *dev;
+
+ list_store = gtk_list_store_new(3,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ devs = cc_usbdevs_scan();
+ if (devs) {
+ for (n = 0; n < devs->ndev; n++) {
+ dev = devs->dev[n];
+ gtk_list_store_append(list_store, &iter);
+ gtk_list_store_set(list_store, &iter,
+ 0, dev->product,
+ 1, dev->serial,
+ 2, dev->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);
+ cc_usbdevs_free(devs);
+}
+
+static GtkMessageDialog *dev_open_fail_dialog;
+
+static void
+aoview_dev_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(dev_open_fail_dialog,
+ "\"%s\"", utf8_file);
+ if (utf8_file != name)
+ g_free(utf8_file);
+ gtk_dialog_run(GTK_DIALOG(dev_open_fail_dialog));
+ gtk_widget_hide(GTK_WIDGET(dev_open_fail_dialog));
+}
+
+gboolean dialog_save_log;
+
+static void
+aoview_dev_selected(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *string;
+ gtk_tree_model_get(model, iter,
+ 2, &string,
+ -1);
+ if (dialog_save_log) {
+ dialog_save_log = FALSE;
+ if (!aoview_eeprom_save(string))
+ aoview_dev_open_failed(string);
+ } else {
+ if (!aoview_monitor_connect(string))
+ aoview_dev_open_failed(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();
+}
+
+static void
+aoview_dev_savelog(GtkWidget *widget, gpointer data)
+{
+ dialog_save_log = TRUE;
+ gtk_widget_show(dialog);
+}
+
+#define _(a) a
+
+void
+aoview_dev_dialog_init(GladeXML *xml)
+{
+ GtkTreeView *dev_list;
+ GtkWidget *connect_button;
+ GtkTreeSelection *dev_selection;
+ GtkWidget *ao_disconnect;
+ GtkWidget *ao_savelog;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ ao_savelog = glade_xml_get_widget(xml, "ao_savelog");
+ assert(ao_savelog);
+
+ g_signal_connect(G_OBJECT(ao_savelog), "activate",
+ G_CALLBACK(aoview_dev_savelog),
+ dialog);
+ dev_open_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "dev_open_fail_dialog"));
+ assert(dev_open_fail_dialog);
+}
--- /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 EEPROM_LEN 1024
+
+static struct aoview_file *eeprom_file;
+static char eeprom_line[EEPROM_LEN + 1];
+static int eeprom_pos;
+static GtkMessageDialog *eeprom_save_done;
+static GtkWidget *eeprom_save_close;
+static gboolean eeprom_save_shown;
+
+static void
+aoview_eeprom_disconnect(struct aoview_serial *serial)
+{
+ aoview_file_finish(eeprom_file);
+}
+
+static void
+aoview_eeprom_done(struct aoview_serial *serial)
+{
+ gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
+ "EEPROM data saved");
+ gtk_message_dialog_set_markup(eeprom_save_done,
+ "<b>EEPROM data saved as</b>");
+ if (!eeprom_save_shown)
+ gtk_widget_show(GTK_WIDGET(eeprom_save_done));
+ eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
+ if (eeprom_save_close)
+ gtk_widget_set_sensitive(eeprom_save_close, TRUE);
+ aoview_eeprom_disconnect(serial);
+}
+
+static gboolean
+aoview_eeprom_parse(struct aoview_serial *serial,
+ char *line)
+{
+ char cmd;
+ int tick;
+ int a;
+ int b;
+ int serial_number;
+ const char *name;
+ char *utf8_name;
+
+ if (!strcmp(line, "end")) {
+ aoview_eeprom_done(serial);
+ return FALSE;
+ }
+ if (sscanf(line, "serial-number %u", &serial_number) == 1) {
+ aoview_file_set_serial(eeprom_file, serial_number);
+ } else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) {
+ aoview_file_printf(eeprom_file, "%s\n", line);
+ if (cmd == 'S' && a == 8) {
+ aoview_eeprom_done(serial);
+ return FALSE;
+ }
+
+ if (!eeprom_save_shown)
+ {
+ name = aoview_file_name(eeprom_file);
+ if (name) {
+ utf8_name = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+ if (!utf8_name)
+ utf8_name = (char *) name;
+ gtk_widget_set_sensitive(eeprom_save_close, FALSE);
+ gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
+ "Saving EEPROM data");
+ gtk_message_dialog_set_markup(eeprom_save_done,
+ "<b>Saving EEPROM data as</b>");
+ gtk_message_dialog_format_secondary_text(eeprom_save_done, "%s",
+ utf8_name);
+ if (utf8_name != name)
+ g_free(utf8_name);
+ gtk_container_check_resize(GTK_CONTAINER(eeprom_save_done));
+ gtk_widget_show(GTK_WIDGET(eeprom_save_done));
+ eeprom_save_shown = TRUE;
+ eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
+ if (eeprom_save_close)
+ gtk_widget_set_sensitive(eeprom_save_close, FALSE);
+ }
+ }
+ }
+ return TRUE;
+}
+
+static void
+aoview_eeprom_callback(gpointer user_data,
+ struct aoview_serial *serial,
+ gint revents)
+{
+ int c;
+
+ if (revents & (G_IO_HUP|G_IO_ERR)) {
+ aoview_eeprom_disconnect(serial);
+ return;
+ }
+ if (revents & G_IO_IN) {
+ for (;;) {
+ c = aoview_serial_getc(serial);
+ if (c == -1)
+ break;
+ if (c == '\r')
+ continue;
+ if (c == '\n') {
+ eeprom_line[eeprom_pos] = '\0';
+ if (eeprom_pos)
+ if (!aoview_eeprom_parse(serial, eeprom_line))
+ break;
+ eeprom_pos = 0;
+ } else if (eeprom_pos < EEPROM_LEN)
+ eeprom_line[eeprom_pos++] = c;
+ }
+ }
+}
+
+gboolean
+aoview_eeprom_save(const char *device)
+{
+ struct aoview_serial *serial;
+
+ gtk_widget_hide(GTK_WIDGET(eeprom_save_done));
+ eeprom_save_shown = FALSE;
+ serial = aoview_serial_open(device);
+ if (!serial)
+ return FALSE;
+ aoview_serial_set_callback(serial, aoview_eeprom_callback);
+ aoview_serial_printf(serial, "v\nl\n");
+ return TRUE;
+}
+
+void
+aoview_eeprom_init(GladeXML *xml)
+{
+ eeprom_file = aoview_file_new("eeprom");
+ assert(eeprom_file);
+
+ eeprom_save_done = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "ao_save_done"));
+ assert(eeprom_save_done);
+
+}
--- /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_file_dir;
+
+#define ALTOS_DIR_PATH "/apps/aoview/log_dir"
+#define DEFAULT_DIR "AltOS"
+
+struct aoview_file {
+ char *ext;
+ FILE *file;
+ char *name;
+ int failed;
+ int serial;
+ int sequence;
+};
+
+static void
+aoview_file_save_conf(void)
+{
+ GConfClient *gconf_client;
+
+ gconf_client = gconf_client_get_default();
+ if (gconf_client)
+ {
+ gconf_client_set_string(gconf_client,
+ ALTOS_DIR_PATH,
+ aoview_file_dir,
+ NULL);
+ g_object_unref(G_OBJECT(gconf_client));
+ }
+}
+
+static void
+aoview_file_configure(GtkWidget *widget, gpointer data)
+{
+ GtkFileChooser *chooser = data;
+ aoview_file_dir = gtk_file_chooser_get_filename(chooser);
+ aoview_file_save_conf();
+ gtk_widget_hide(GTK_WIDGET(chooser));
+}
+
+void
+aoview_file_finish(struct aoview_file *file)
+{
+ if (file->file) {
+ fclose(file->file);
+ file->file = NULL;
+ free(file->name);
+ file->name = NULL;
+ }
+ file->failed = 0;
+}
+
+const char *
+aoview_file_name(struct aoview_file *file)
+{
+ return file->name;
+}
+
+static GtkMessageDialog *file_fail_dialog;
+
+static void
+aoview_file_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(file_fail_dialog,
+ "\"%s\"", utf8_file);
+ if (utf8_file != name)
+ g_free(utf8_file);
+ gtk_widget_show(GTK_WIDGET(file_fail_dialog));
+}
+
+gboolean
+aoview_file_start(struct aoview_file *file)
+{
+ char base[50];
+ struct tm tm;
+ time_t now;
+ char *full;
+ int r;
+
+ if (file->file)
+ return TRUE;
+
+ if (file->failed)
+ return FALSE;
+
+ now = time(NULL);
+ (void) localtime_r(&now, &tm);
+ aoview_mkdir(aoview_file_dir);
+ for (;;) {
+ snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d.%s",
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ file->serial,
+ file->sequence,
+ file->ext);
+ full = aoview_fullname(aoview_file_dir, base);
+ r = access(full, F_OK);
+ if (r < 0) {
+ file->file = fopen(full, "w");
+ if (!file->file) {
+ aoview_file_open_failed(full);
+ free(full);
+ file->failed = 1;
+ return FALSE;
+ } else {
+ setlinebuf(file->file);
+ file->name = full;
+ return TRUE;
+ }
+ }
+ free(full);
+ file->sequence++;
+ }
+}
+
+void
+aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap)
+{
+ if (!aoview_file_start(file))
+ return;
+ vfprintf(file->file, format, ap);
+}
+
+void
+aoview_file_printf(struct aoview_file *file, char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ aoview_file_vprintf(file, format, ap);
+ va_end(ap);
+}
+
+struct aoview_file *
+aoview_file_new(char *ext)
+{
+ struct aoview_file *file;
+
+ file = calloc (1, sizeof (struct aoview_file));
+ if (!file)
+ return NULL;
+ file->ext = strdup(ext);
+ if (!file->ext) {
+ free(file);
+ return NULL;
+ }
+ return file;
+}
+
+void
+aoview_file_destroy(struct aoview_file *file)
+{
+ if (file->file)
+ fclose(file->file);
+ if (file->name)
+ free(file->name);
+ free(file->ext);
+ free(file);
+}
+
+void
+aoview_file_set_serial(struct aoview_file *file, int serial)
+{
+ if (serial != file->serial)
+ aoview_file_finish(file);
+ file->serial = serial;
+}
+
+int
+aoview_file_get_serial(struct aoview_file *file)
+{
+ return file->serial;
+}
+
+void
+aoview_file_init(GladeXML *xml)
+{
+ GConfClient *gconf_client;
+ char *file_dir = NULL;
+ GtkFileChooser *file_chooser_dialog;
+ GtkWidget *file_configure_ok;
+
+ g_type_init();
+ gconf_client = gconf_client_get_default();
+ if (gconf_client)
+ {
+ file_dir = gconf_client_get_string(gconf_client,
+ ALTOS_DIR_PATH,
+ NULL);
+ g_object_unref(G_OBJECT(gconf_client));
+ }
+ if (!file_dir) {
+ aoview_file_dir = aoview_fullname(getenv("HOME"), DEFAULT_DIR);
+ aoview_file_save_conf();
+ } else {
+ aoview_file_dir = strdup(file_dir);
+ }
+
+ file_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "file_chooser_dialog"));
+ assert(file_chooser_dialog);
+ gtk_file_chooser_set_filename(file_chooser_dialog, aoview_file_dir);
+
+ file_configure_ok = glade_xml_get_widget(xml, "file_configure_ok");
+ assert(file_configure_ok);
+
+ g_signal_connect(G_OBJECT(file_configure_ok), "clicked",
+ G_CALLBACK(aoview_file_configure),
+ file_chooser_dialog);
+
+
+ file_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "file_fail_dialog"));
+ assert(file_fail_dialog);
+}
--- /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 <stdio.h>
+#include <flite/flite.h>
+#include "aoview.h"
+#include <alsa/asoundlib.h>
+
+cst_voice *register_cmu_us_kal();
+static cst_voice *voice;
+
+static FILE *pipe_write;
+static GThread *aoview_flite_thread;
+
+static snd_pcm_t *alsa_handle;
+
+gpointer
+aoview_flite_task(gpointer data)
+{
+ FILE *input = data;
+ char line[1024];
+ cst_wave *wave;
+ int rate;
+ int channels;
+ int err;
+ char *samples;
+ int num_samples;
+
+ err = snd_pcm_open(&alsa_handle, "default",
+ SND_PCM_STREAM_PLAYBACK, 0);
+ if (err >= 0)
+ {
+ if (err < 0) {
+ snd_pcm_close(alsa_handle);
+ alsa_handle = 0;
+ }
+ }
+ rate = 0;
+ channels = 0;
+ while (fgets(line, sizeof (line) - 1, input) != NULL) {
+ if (!alsa_handle)
+ continue;
+ wave = flite_text_to_wave(line, voice);
+ if (wave->sample_rate != rate ||
+ wave->num_channels != channels)
+ {
+ rate = wave->sample_rate;
+ channels = wave->num_channels;
+ err = snd_pcm_set_params(alsa_handle,
+ SND_PCM_FORMAT_S16,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ channels,
+ rate,
+ 1,
+ 100000);
+ if (err < 0)
+ fprintf(stderr, "alsa set_params error %s\n",
+ strerror(-err));
+ }
+ err = snd_pcm_prepare(alsa_handle);
+ if (err < 0)
+ fprintf(stderr, "alsa pcm_prepare error %s\n",
+ strerror(-err));
+ samples = (char *) wave->samples;
+ num_samples = wave->num_samples;
+ while (num_samples > 0) {
+ err = snd_pcm_writei(alsa_handle,
+ samples, num_samples);
+ if (err <= 0) {
+ fprintf(stderr, "alsa write error %s\n",
+ strerror(-err));
+ break;
+ }
+ num_samples -= err;
+ samples += err * 2 * channels;
+ }
+ snd_pcm_drain(alsa_handle);
+ delete_wave(wave);
+ }
+ snd_pcm_close(alsa_handle);
+ alsa_handle = 0;
+ return NULL;
+}
+
+void
+aoview_flite_stop(void)
+{
+ int status;
+ if (pipe_write) {
+ fclose(pipe_write);
+ pipe_write = NULL;
+ }
+ if (aoview_flite_thread) {
+ g_thread_join(aoview_flite_thread);
+ aoview_flite_thread = NULL;
+ }
+}
+
+FILE *
+aoview_flite_start(void)
+{
+ static once;
+ int p[2];
+ GError *error;
+ FILE *pipe_read;
+
+ if (!once) {
+ flite_init();
+ voice = register_cmu_us_kal();
+ if (!voice) {
+ perror("register voice");
+ exit(1);
+ }
+ }
+ aoview_flite_stop();
+ pipe(p);
+ pipe_read = fdopen(p[0], "r");
+ pipe_write = fdopen(p[1], "w");
+ g_thread_create(aoview_flite_task, pipe_read, TRUE, &error);
+ return pipe_write;
+}
--- /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 struct {
+ char *name;
+ char *initial_value;
+ GtkLabel *widget;
+} label_widgets[] = {
+ { "height_label", "Height (m)", NULL },
+ { "state_label", "State", NULL },
+ { "rssi_label", "RSSI (dBm)", NULL },
+ { "speed_label", "Speed (m/s)", NULL },
+ { "height_value", "0", NULL },
+ { "state_value", "pad", NULL },
+ { "rssi_value", "-50", NULL },
+ { "speed_value", "0", NULL },
+};
+
+static void
+aoview_label_assign(GtkLabel *widget, char *value)
+{
+ char *markup;
+
+ markup = g_markup_printf_escaped("<span font_weight=\"bold\" size=\"xx-large\">%s</span>", value);
+ gtk_label_set_markup(widget, markup);
+ g_free(markup);
+}
+
+void
+aoview_label_show(struct aostate *state)
+{
+ char line[1024];
+ sprintf(line, "%d", state->height);
+ aoview_label_assign(label_widgets[4].widget, line);
+
+ aoview_label_assign(label_widgets[5].widget, state->data.state);
+
+ sprintf(line, "%d", state->data.rssi);
+ aoview_label_assign(label_widgets[6].widget, line);
+
+ if (state->ascent)
+ sprintf(line, "%6.0f", fabs(state->speed));
+ else
+ sprintf(line, "%6.0f", fabs(state->baro_speed));
+ aoview_label_assign(label_widgets[7].widget, line);
+}
+
+void
+aoview_label_init(GladeXML *xml)
+{
+ int i;
+ for (i = 0; i < sizeof(label_widgets)/sizeof(label_widgets[0]); i++) {
+ label_widgets[i].widget = GTK_LABEL(glade_xml_get_widget(xml, label_widgets[i].name));
+ aoview_label_assign(label_widgets[i].widget, label_widgets[i].initial_value);
+ assert(label_widgets[i].widget);
+ }
+}
--- /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 struct aoview_file *aoview_log;
+
+void
+aoview_log_new(void)
+{
+ aoview_file_finish(aoview_log);
+ aoview_state_new();
+}
+
+void
+aoview_log_set_serial(int serial)
+{
+ aoview_file_set_serial(aoview_log, serial);
+}
+
+int
+aoview_log_get_serial(void)
+{
+ return aoview_file_get_serial(aoview_log);
+}
+
+void
+aoview_log_printf(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ aoview_file_vprintf(aoview_log, format, ap);
+ va_end(ap);
+}
+
+static void
+aoview_log_new_item(GtkWidget *widget, gpointer data)
+{
+ aoview_file_finish(aoview_log);
+}
+
+void
+aoview_log_init(GladeXML *xml)
+{
+ GtkWidget *log_new;
+
+ aoview_log = aoview_file_new("telem");
+ assert(aoview_log);
+
+ log_new = glade_xml_get_widget(xml, "log_new");
+ assert(log_new);
+ g_signal_connect(G_OBJECT(log_new), "activate",
+ G_CALLBACK(aoview_log_new_item),
+ NULL);
+}
--- /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 const char aoview_glade[] = {
+#include "aoview_glade.h"
+};
+
+static void usage(void) {
+ printf("aoview [--device|-d device_file]");
+ exit(1);
+}
+
+static void destroy_event(GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit();
+}
+
+extern int _Xdebug;
+char *aoview_tty = NULL;
+
+int main(int argc, char **argv)
+{
+ GladeXML *xml = NULL;
+ GtkWidget *mainwindow;
+ GtkAboutDialog *about_dialog;
+
+ static struct option long_options[] = {
+ { "tty", 1, 0, 'T'},
+ { "sync", 0, 0, 's'},
+ { 0, 0, 0, 0 }
+ };
+ for (;;) {
+ int c, temp;
+
+ c = getopt_long_only(argc, argv, "sT:", long_options, &temp);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'T':
+ aoview_tty = optarg;
+ break;
+ case 's':
+ _Xdebug = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ g_thread_init(NULL);
+ gtk_init(&argc, &argv);
+ glade_init();
+
+ xml = glade_xml_new_from_buffer(aoview_glade, sizeof (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");
+ assert(mainwindow);
+
+ g_signal_connect (G_OBJECT(mainwindow), "destroy",
+ G_CALLBACK(destroy_event), NULL);
+
+ about_dialog = GTK_ABOUT_DIALOG(glade_xml_get_widget(xml, "about_dialog"));
+ assert(about_dialog);
+ gtk_about_dialog_set_version(about_dialog, AOVIEW_VERSION);
+
+ aoview_voice_init(xml);
+
+ aoview_dev_dialog_init(xml);
+
+ aoview_state_init(xml);
+
+ aoview_file_init(xml);
+
+ aoview_log_init(xml);
+
+ aoview_table_init(xml);
+
+ aoview_eeprom_init(xml);
+
+ aoview_replay_init(xml);
+
+ aoview_label_init(xml);
+
+ if (aoview_tty) {
+ if (!aoview_monitor_connect(aoview_tty)) {
+ perror(aoview_tty);
+ exit(1);
+ }
+ }
+ aoview_voice_speak("rocket flight monitor ready\n");
+
+ gtk_main();
+
+ return 0;
+}
--- /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 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;
+ }
+ aoview_log_new();
+}
+
+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_hex(int *target, char *source)
+{
+ *target = strtol(source, NULL, 16);
+}
+
+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;
+}
+
+#define PARSE_MAX_WORDS 256
+
+gboolean
+aoview_monitor_parse(const char *input_line)
+{
+ char *saveptr;
+ char *words[PARSE_MAX_WORDS];
+ int nword;
+ char line_buf[8192], *line;
+ struct aodata data;
+ int tracking_pos;
+
+ /* avoid smashing our input parameter */
+ strncpy (line_buf, input_line, sizeof (line_buf)-1);
+ line_buf[sizeof(line_buf) - 1] = '\0';
+ line = line_buf;
+ for (nword = 0; nword < PARSE_MAX_WORDS; nword++) {
+ words[nword] = strtok_r(line, " \t\n", &saveptr);
+ line = NULL;
+ if (words[nword] == NULL)
+ break;
+ }
+ if (nword < 36)
+ return FALSE;
+ if (strcmp(words[0], "CALL") != 0)
+ return FALSE;
+ aoview_parse_string(data.callsign, sizeof (data.callsign), words[1]);
+ aoview_parse_int(&data.serial, words[3]);
+
+ aoview_parse_int(&data.rssi, words[5]);
+ aoview_parse_string(data.state, sizeof (data.state), words[9]);
+ aoview_parse_int(&data.tick, words[10]);
+ aoview_parse_int(&data.accel, words[12]);
+ aoview_parse_int(&data.pres, words[14]);
+ aoview_parse_int(&data.temp, words[16]);
+ aoview_parse_int(&data.batt, words[18]);
+ aoview_parse_int(&data.drogue, words[20]);
+ aoview_parse_int(&data.main, words[22]);
+ aoview_parse_int(&data.flight_accel, words[24]);
+ aoview_parse_int(&data.ground_accel, words[26]);
+ aoview_parse_int(&data.flight_vel, words[28]);
+ aoview_parse_int(&data.flight_pres, words[30]);
+ aoview_parse_int(&data.ground_pres, words[32]);
+ aoview_parse_int(&data.gps.nsat, words[34]);
+ if (strcmp (words[36], "unlocked") == 0) {
+ data.gps.gps_connected = 1;
+ data.gps.gps_locked = 0;
+ data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
+ data.gps.lat = data.gps.lon = 0;
+ data.gps.alt = 0;
+ tracking_pos = 37;
+ } else if (nword >= 40) {
+ data.gps.gps_locked = 1;
+ data.gps.gps_connected = 1;
+ sscanf(words[36], "%d:%d:%d", &data.gps.gps_time.hour, &data.gps.gps_time.minute, &data.gps.gps_time.second);
+ aoview_parse_pos(&data.gps.lat, words[37]);
+ aoview_parse_pos(&data.gps.lon, words[38]);
+ sscanf(words[39], "%dm", &data.gps.alt);
+ tracking_pos = 46;
+ } else {
+ data.gps.gps_connected = 0;
+ data.gps.gps_locked = 0;
+ data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
+ data.gps.lat = data.gps.lon = 0;
+ data.gps.alt = 0;
+ tracking_pos = -1;
+ }
+ if (nword >= 46) {
+ data.gps.gps_extended = 1;
+ sscanf(words[40], "%lfm/s", &data.gps.ground_speed);
+ sscanf(words[41], "%d", &data.gps.course);
+ sscanf(words[42], "%lfm/s", &data.gps.climb_rate);
+ sscanf(words[43], "%lf", &data.gps.hdop);
+ sscanf(words[44], "%d", &data.gps.h_error);
+ sscanf(words[45], "%d", &data.gps.v_error);
+ } else {
+ data.gps.gps_extended = 0;
+ data.gps.ground_speed = 0;
+ data.gps.course = 0;
+ data.gps.climb_rate = 0;
+ data.gps.hdop = 0;
+ data.gps.h_error = 0;
+ data.gps.v_error = 0;
+ }
+ if (tracking_pos >= 0 && nword >= tracking_pos + 2 && strcmp(words[tracking_pos], "SAT") == 0) {
+ int c, n, pos;
+ aoview_parse_int(&n, words[tracking_pos + 1]);
+ pos = tracking_pos + 2;
+ if (nword >= pos + n * 3) {
+ data.gps_tracking.channels = n;
+ for (c = 0; c < n; c++) {
+ aoview_parse_int(&data.gps_tracking.sats[c].svid,
+ words[pos + 0]);
+ aoview_parse_hex(&data.gps_tracking.sats[c].state,
+ words[pos + 1]);
+ aoview_parse_int(&data.gps_tracking.sats[c].c_n0,
+ words[pos + 2]);
+ pos += 3;
+ }
+ } else {
+ data.gps_tracking.channels = 0;
+ }
+ } else {
+ data.gps_tracking.channels = 0;
+ }
+ aoview_state_notify(&data);
+ return TRUE;
+}
+
+static void
+aoview_monitor_callback(gpointer user_data,
+ struct aoview_serial *serial,
+ gint revents)
+{
+ int c;
+
+ if (revents & (G_IO_HUP|G_IO_ERR)) {
+ aoview_monitor_disconnect();
+ return;
+ }
+ if (revents & G_IO_IN) {
+ for (;;) {
+ c = aoview_serial_getc(serial);
+ if (c == -1)
+ break;
+ if (c == '\r')
+ continue;
+ if (c == '\n') {
+ monitor_line[monitor_pos] = '\0';
+ if (monitor_pos) {
+ if (aoview_monitor_parse(monitor_line)) {
+ aoview_log_set_serial(aostate.data.serial);
+ if (aoview_log_get_serial())
+ aoview_log_printf ("%s\n", monitor_line);
+ }
+ }
+ monitor_pos = 0;
+ } else if (monitor_pos < MONITOR_LEN)
+ monitor_line[monitor_pos++] = c;
+ }
+ }
+}
+
+gboolean
+aoview_monitor_connect(char *tty)
+{
+ aoview_monitor_disconnect();
+ monitor_serial = aoview_serial_open(tty);
+ if (!monitor_serial)
+ return FALSE;
+ aoview_table_clear();
+ aoview_state_reset();
+ aoview_serial_set_callback(monitor_serial,
+ aoview_monitor_callback);
+ return TRUE;
+}
--- /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 GtkFileChooser *replay_dialog;
+static GtkWidget *replay_ok;
+static FILE *replay_file;
+static int replay_tick;
+
+static int
+find_tick(char *line, gboolean *is_pad)
+{
+ char *state = strstr(line, "STATE");
+ if (!state)
+ return -1;
+ state = strchr(state, ' ');
+ if (!state)
+ return -1;
+ while (*state == ' ')
+ state++;
+ *is_pad = strncmp(state, "pad", 3) == 0;
+ while (*state && !isdigit(*state))
+ state++;
+ return atoi(state);
+}
+
+static void
+aoview_replay_close(void)
+{
+ if (replay_file) {
+ fclose(replay_file);
+ replay_file = NULL;
+ }
+}
+
+static char replay_line[1024];
+
+static gboolean
+aoview_replay_read(gpointer data);
+
+static gboolean
+aoview_replay_execute(gpointer data)
+{
+ aoview_monitor_parse(replay_line);
+ g_idle_add(aoview_replay_read, NULL);
+ return FALSE;
+}
+
+static gboolean
+aoview_replay_read(gpointer data)
+{
+ int tick;
+ gboolean is_pad;
+
+ if (!replay_file)
+ return FALSE;
+ if (fgets(replay_line, sizeof (replay_line), replay_file)) {
+ tick = find_tick(replay_line, &is_pad);
+ if (tick >= 0 && replay_tick >= 0 && !is_pad) {
+ while (tick < replay_tick)
+ tick += 65536;
+ g_timeout_add((tick - replay_tick) * 10,
+ aoview_replay_execute,
+ NULL);
+ } else {
+ aoview_replay_execute(NULL);
+ }
+ replay_tick = tick;
+ } else {
+ aoview_replay_close();
+ }
+ return FALSE;
+}
+
+static void
+aoview_replay_open(GtkWidget *widget, gpointer data)
+{
+ char *replay_file_name;
+ GtkWidget *dialog;
+
+ aoview_replay_close();
+ replay_file_name = gtk_file_chooser_get_filename(replay_dialog);
+ replay_file = fopen(replay_file_name, "r");
+ if (!replay_file) {
+ dialog = gtk_message_dialog_new(GTK_WINDOW(replay_dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "Error loading file '%s': %s",
+ replay_file_name, g_strerror(errno));
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ } else {
+ replay_tick = -1;
+ aoview_state_reset();
+ aoview_replay_read(NULL);
+ }
+ gtk_widget_hide(GTK_WIDGET(replay_dialog));
+}
+
+void
+aoview_replay_init(GladeXML *xml)
+{
+ GtkFileFilter *telem_filter;
+ GtkFileFilter *all_filter;
+ GtkFileFilter *log_filter;
+
+ telem_filter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(telem_filter, "*.telem");
+ gtk_file_filter_set_name(telem_filter, "Telemetry Files");
+
+ log_filter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(log_filter, "*.log");
+ gtk_file_filter_set_name(log_filter, "Log Files");
+
+ all_filter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(all_filter, "*");
+ gtk_file_filter_set_name(all_filter, "All Files");
+
+ replay_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "ao_replay_dialog"));
+ assert(replay_dialog);
+ gtk_file_chooser_set_current_folder(replay_dialog, aoview_file_dir);
+ gtk_file_chooser_add_filter(replay_dialog, telem_filter);
+ gtk_file_chooser_add_filter(replay_dialog, log_filter);
+ gtk_file_chooser_add_filter(replay_dialog, all_filter);
+
+ replay_ok = glade_xml_get_widget(xml, "ao_replay_ok");
+ assert(replay_ok);
+ g_signal_connect(G_OBJECT(replay_ok), "clicked",
+ G_CALLBACK(aoview_replay_open),
+ replay_dialog);
+}
--- /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"
+#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;
+ aoview_serial_callback func = (aoview_serial_callback) callback;
+ 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 (func)
+ (*func)(user_data, serial, revents);
+ 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) {
+ g_source_destroy(&serial->source);
+ 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);
+ aoview_serial_set_callback(serial, NULL);
+ return serial;
+}
+
+void
+aoview_serial_close(struct aoview_serial *serial)
+{
+ g_source_remove_poll(&serial->source, &serial->poll_fd);
+ close(serial->fd);
+ g_source_destroy(&serial->source);
+}
+
+void
+aoview_serial_set_callback(struct aoview_serial *serial,
+ aoview_serial_callback func)
+{
+ g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL);
+}
--- /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"
+#include <math.h>
+
+static inline double sqr(double a) { return a * a; };
+
+static void
+aoview_great_circle (double start_lat, double start_lon,
+ double end_lat, double end_lon,
+ double *dist, double *bearing)
+{
+ const double rad = M_PI / 180;
+ const double earth_radius = 6371.2 * 1000; /* in meters */
+ double lat1 = rad * start_lat;
+ double lon1 = rad * -start_lon;
+ double lat2 = rad * end_lat;
+ double lon2 = rad * -end_lon;
+
+ double d_lat = lat2 - lat1;
+ double d_lon = lon2 - lon1;
+
+ /* From http://en.wikipedia.org/wiki/Great-circle_distance */
+ double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
+ sqr(cos(lat1) * sin(lat2) -
+ sin(lat1) * cos(lat2) * cos(d_lon)));
+ double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
+ double d = atan2(vdn,vdd);
+ double course;
+
+ if (cos(lat1) < 1e-20) {
+ if (lat1 > 0)
+ course = M_PI;
+ else
+ course = -M_PI;
+ } else {
+ if (d < 1e-10)
+ course = 0;
+ else
+ course = acos((sin(lat2)-sin(lat1)*cos(d)) /
+ (sin(d)*cos(lat1)));
+ if (sin(lon2-lon1) > 0)
+ course = 2 * M_PI-course;
+ }
+ *dist = d * earth_radius;
+ *bearing = course * 180/M_PI;
+}
+
+static void
+aoview_state_add_deg(int column, char *label, double deg, char pos, char neg)
+{
+ double int_part;
+ double min;
+ char sign = pos;
+
+ if (deg < 0) {
+ deg = -deg;
+ sign = neg;
+ }
+ int_part = floor (deg);
+ min = (deg - int_part) * 60.0;
+ aoview_table_add_row(column, label, "%d°%lf'%c",
+ (int) int_part, min, sign);
+
+}
+
+static char *ascent_states[] = {
+ "boost",
+ "fast",
+ "coast",
+ 0,
+};
+
+static double
+aoview_time(void)
+{
+ struct timespec now;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return (double) now.tv_sec + (double) now.tv_nsec / 1.0e9;
+}
+
+/*
+ * Fill out the derived data fields
+ */
+static void
+aoview_state_derive(struct aodata *data, struct aostate *state)
+{
+ int i;
+ double new_height;
+ double height_change;
+ double time_change;
+ int tick_count;
+
+ state->report_time = aoview_time();
+
+ state->prev_data = state->data;
+ state->prev_npad = state->npad;
+ state->data = *data;
+ tick_count = data->tick;
+ if (tick_count < state->prev_data.tick)
+ tick_count += 65536;
+ time_change = (tick_count - state->prev_data.tick) / 100.0;
+
+ state->ground_altitude = aoview_pres_to_altitude(data->ground_pres);
+ new_height = aoview_pres_to_altitude(data->flight_pres) - state->ground_altitude;
+ height_change = new_height - state->height;
+ state->height = new_height;
+ if (time_change)
+ state->baro_speed = (state->baro_speed * 3 + (height_change / time_change)) / 4.0;
+ state->acceleration = (data->ground_accel - data->flight_accel) / 27.0;
+ state->speed = data->flight_vel / 2700.0;
+ state->temperature = ((data->temp / 32767.0 * 3.3) - 0.5) / 0.01;
+ state->drogue_sense = data->drogue / 32767.0 * 15.0;
+ state->main_sense = data->main / 32767.0 * 15.0;
+ state->battery = data->batt / 32767.0 * 5.0;
+ if (!strcmp(data->state, "pad")) {
+ if (data->gps.gps_locked && data->gps.nsat >= 4) {
+ state->npad++;
+ state->pad_lat_total += data->gps.lat;
+ state->pad_lon_total += data->gps.lon;
+ state->pad_alt_total += data->gps.alt;
+ if (state->npad > 1) {
+ state->pad_lat = (state->pad_lat * 31 + data->gps.lat) / 32.0;
+ state->pad_lon = (state->pad_lon * 31 + data->gps.lon) / 32.0;
+ state->pad_alt = (state->pad_alt * 31 + data->gps.alt) / 32.0;
+ } else {
+ state->pad_lat = data->gps.lat;
+ state->pad_lon = data->gps.lon;
+ state->pad_alt = data->gps.alt;
+ }
+ }
+ }
+ state->ascent = FALSE;
+ for (i = 0; ascent_states[i]; i++)
+ if (!strcmp(data->state, ascent_states[i]))
+ state->ascent = TRUE;
+
+ /* Only look at accelerometer data on the way up */
+ if (state->ascent && state->acceleration > state->max_acceleration)
+ state->max_acceleration = state->acceleration;
+ if (state->ascent && state->speed > state->max_speed)
+ state->max_speed = state->speed;
+
+ if (state->height > state->max_height)
+ state->max_height = state->height;
+ state->gps.gps_locked = data->gps.gps_locked;
+ state->gps.gps_connected = data->gps.gps_connected;
+ if (data->gps.gps_locked) {
+ state->gps = data->gps;
+ state->gps_valid = 1;
+ if (state->npad)
+ aoview_great_circle(state->pad_lat, state->pad_lon, state->gps.lat, state->gps.lon,
+ &state->distance, &state->bearing);
+ }
+ if (data->gps_tracking.channels)
+ state->gps_tracking = data->gps_tracking;
+ if (state->npad) {
+ state->gps_height = state->gps.alt - state->pad_alt;
+ } else {
+ state->gps_height = 0;
+ }
+}
+
+void
+aoview_speak_state(struct aostate *state)
+{
+ if (strcmp(state->data.state, state->prev_data.state)) {
+ aoview_voice_speak("%s\n", state->data.state);
+ if (!strcmp(state->data.state, "drogue"))
+ aoview_voice_speak("apogee %d meters\n",
+ (int) state->max_height);
+ if (!strcmp(state->prev_data.state, "boost"))
+ aoview_voice_speak("max speed %d meters per second\n",
+ (int) state->max_speed);
+ }
+ if (state->prev_npad < MIN_PAD_SAMPLES && state->npad >= MIN_PAD_SAMPLES)
+ aoview_voice_speak("g p s ready\n");
+}
+
+void
+aoview_speak_height(struct aostate *state)
+{
+ aoview_voice_speak("%d meters\n", state->height);
+}
+
+struct aostate aostate;
+
+static guint aostate_timeout;
+
+#define COMPASS_LIMIT(n) ((n * 22.5) + 22.5/2)
+
+static char *compass_points[] = {
+ "north",
+ "north north east",
+ "north east",
+ "east north east",
+ "east",
+ "east south east",
+ "south east",
+ "south south east",
+ "south",
+ "south south west",
+ "south west",
+ "west south west",
+ "west",
+ "west north west",
+ "north west",
+ "north north west",
+};
+
+static char *
+aoview_compass_point(double bearing)
+{
+ int i;
+ while (bearing < 0)
+ bearing += 360.0;
+ while (bearing >= 360.0)
+ bearing -= 360.0;
+
+ i = floor ((bearing - 22.5/2) / 22.5 + 0.5);
+ if (i < 0) i = 0;
+ if (i >= sizeof (compass_points) / sizeof (compass_points[0]))
+ i = 0;
+ return compass_points[i];
+}
+
+static gboolean
+aoview_state_timeout(gpointer data)
+{
+ double now = aoview_time();
+
+ if (strlen(aostate.data.state) > 0 && strcmp(aostate.data.state, "pad") != 0)
+ aoview_speak_height(&aostate);
+ if (now - aostate.report_time >= 20 || !strcmp(aostate.data.state, "landed")) {
+ if (!aostate.ascent) {
+ if (fabs(aostate.baro_speed) < 20 && aostate.height < 100)
+ aoview_voice_speak("rocket landed safely\n");
+ else
+ aoview_voice_speak("rocket may have crashed\n");
+ if (aostate.gps_valid) {
+ aoview_voice_speak("rocket reported %s of pad distance %d meters\n",
+ aoview_compass_point(aostate.bearing),
+ (int) aostate.distance);
+ }
+ }
+ aostate_timeout = 0;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+aoview_state_reset(void)
+{
+ memset(&aostate, '\0', sizeof (aostate));
+}
+
+void
+aoview_state_notify(struct aodata *data)
+{
+ struct aostate *state = &aostate;
+ aoview_state_derive(data, state);
+ aoview_table_start();
+
+ if (state->npad >= MIN_PAD_SAMPLES)
+ aoview_table_add_row(0, "Ground state", "ready");
+ else
+ aoview_table_add_row(0, "Ground state", "waiting for gps (%d)",
+ MIN_PAD_SAMPLES - state->npad);
+ aoview_table_add_row(0, "Rocket state", "%s", state->data.state);
+ aoview_table_add_row(0, "Callsign", "%s", state->data.callsign);
+ aoview_table_add_row(0, "Rocket serial", "%d", state->data.serial);
+
+ aoview_table_add_row(0, "RSSI", "%6ddBm", state->data.rssi);
+ aoview_table_add_row(0, "Height", "%6dm", state->height);
+ aoview_table_add_row(0, "Max height", "%6dm", state->max_height);
+ aoview_table_add_row(0, "Acceleration", "%7.1fm/s²", state->acceleration);
+ aoview_table_add_row(0, "Max acceleration", "%7.1fm/s²", state->max_acceleration);
+ aoview_table_add_row(0, "Speed", "%7.1fm/s", state->ascent ? state->speed : state->baro_speed);
+ aoview_table_add_row(0, "Max Speed", "%7.1fm/s", state->max_speed);
+ aoview_table_add_row(0, "Temperature", "%6.2f°C", state->temperature);
+ aoview_table_add_row(0, "Battery", "%5.2fV", state->battery);
+ aoview_table_add_row(0, "Drogue", "%5.2fV", state->drogue_sense);
+ aoview_table_add_row(0, "Main", "%5.2fV", state->main_sense);
+ aoview_table_add_row(0, "Pad altitude", "%dm", state->ground_altitude);
+ aoview_table_add_row(1, "Satellites", "%d", state->gps.nsat);
+ if (state->gps.gps_locked) {
+ aoview_table_add_row(1, "GPS", "locked");
+ } else if (state->gps.gps_connected) {
+ aoview_table_add_row(1, "GPS", "unlocked");
+ } else {
+ aoview_table_add_row(1, "GPS", "not available");
+ }
+ if (state->gps_valid) {
+ aoview_state_add_deg(1, "Latitude", state->gps.lat, 'N', 'S');
+ aoview_state_add_deg(1, "Longitude", state->gps.lon, 'E', 'W');
+ aoview_table_add_row(1, "GPS altitude", "%d", state->gps.alt);
+ aoview_table_add_row(1, "GPS height", "%d", state->gps_height);
+ aoview_table_add_row(1, "GPS time", "%02d:%02d:%02d",
+ state->gps.gps_time.hour,
+ state->gps.gps_time.minute,
+ state->gps.gps_time.second);
+ }
+ if (state->gps.gps_extended) {
+ aoview_table_add_row(1, "GPS ground speed", "%7.1fm/s %d°",
+ state->gps.ground_speed,
+ state->gps.course);
+ aoview_table_add_row(1, "GPS climb rate", "%7.1fm/s",
+ state->gps.climb_rate);
+ aoview_table_add_row(1, "GPS precision", "%4.1f(hdop) %3dm(h) %3dm(v)",
+ state->gps.hdop, state->gps.h_error, state->gps.v_error);
+ }
+ if (state->npad) {
+ aoview_table_add_row(1, "Distance from pad", "%5.0fm", state->distance);
+ aoview_table_add_row(1, "Direction from pad", "%4.0f°", state->bearing);
+ aoview_state_add_deg(1, "Pad latitude", state->pad_lat, 'N', 'S');
+ aoview_state_add_deg(1, "Pad longitude", state->pad_lon, 'E', 'W');
+ aoview_table_add_row(1, "Pad GPS alt", "%gm", state->pad_alt);
+ }
+ if (state->gps.gps_connected) {
+ int nsat_vis = 0;
+ int nsat_locked = 0;
+ int c;
+
+ for (c = 0; c < state->gps_tracking.channels; c++) {
+ if ((state->gps_tracking.sats[c].state & 0xff) == 0xbf)
+ nsat_locked++;
+ }
+ aoview_table_add_row(2, "Satellites Visible", "%d", state->gps_tracking.channels);
+ aoview_table_add_row(2, "Satellites Locked", "%d", nsat_locked);
+ for (c = 0; c < state->gps_tracking.channels; c++) {
+ aoview_table_add_row(2, "Satellite id,state,C/N0",
+ "%3d,%02x,%2d%s",
+ state->gps_tracking.sats[c].svid,
+ state->gps_tracking.sats[c].state,
+ state->gps_tracking.sats[c].c_n0,
+ (state->gps_tracking.sats[c].state & 0xff) == 0xbf ?
+ " LOCKED" : "");
+ }
+ }
+ aoview_table_finish();
+ aoview_label_show(state);
+ aoview_speak_state(state);
+ if (!aostate_timeout && strcmp(state->data.state, "pad") != 0)
+ aostate_timeout = g_timeout_add_seconds(10, aoview_state_timeout, NULL);
+}
+
+void
+aoview_state_new(void)
+{
+}
+
+void
+aoview_state_init(GladeXML *xml)
+{
+ aoview_state_new();
+}
--- /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 NCOL 3
+
+static GtkTreeView *dataview[NCOL];
+static GtkListStore *datalist[NCOL];
+
+void
+aoview_table_start(void)
+{
+ int col;
+ for (col = 0; col < NCOL; col++)
+ datalist[col] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+void
+aoview_table_add_row(int col, 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[col], &iter);
+ gtk_list_store_set(datalist[col], &iter,
+ 0, label,
+ 1, buf,
+ -1);
+}
+
+void
+aoview_table_finish(void)
+{
+ int col;
+ for (col = 0; col < NCOL; col++) {
+ gtk_tree_view_set_model(dataview[col], GTK_TREE_MODEL(datalist[col]));
+ g_object_unref(G_OBJECT(datalist[col]));
+ gtk_tree_view_columns_autosize(dataview[col]);
+ }
+}
+
+void
+aoview_table_clear(void)
+{
+ int col;
+ for (col = 0; col < NCOL; col++)
+ gtk_tree_view_set_model(dataview[col], NULL);
+}
+
+void
+aoview_table_init(GladeXML *xml)
+{
+ int col;
+
+ for (col = 0; col < NCOL; col++) {
+ char name[32];
+ sprintf(name, "dataview_%d", col);
+ dataview[col] = GTK_TREE_VIEW(glade_xml_get_widget(xml, name));
+ assert(dataview[col]);
+
+ aoview_add_plain_text_column(dataview[col], "Field", 0, 20);
+ aoview_add_plain_text_column(dataview[col], "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;
+}
--- /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"
+
+#if HAVE_FLITE
+#include <stdarg.h>
+
+FILE *aoview_flite;
+
+void aoview_voice_open(void)
+{
+ int err;
+
+ if (!aoview_flite)
+ aoview_flite = aoview_flite_start();
+}
+
+void aoview_voice_close(void)
+{
+ if (aoview_flite) {
+ aoview_flite_stop();
+ aoview_flite = NULL;
+ }
+}
+
+void aoview_voice_speak(char *format, ...)
+{
+ va_list ap;
+
+ if (aoview_flite) {
+ va_start(ap, format);
+ vfprintf(aoview_flite, format, ap);
+ fflush(aoview_flite);
+ va_end(ap);
+ }
+}
+
+#else
+void aoview_voice_open(void)
+{
+}
+
+void aoview_voice_close(void)
+{
+}
+
+void aoview_voice_speak(char *format, ...)
+{
+}
+#endif
+
+
+static GtkCheckMenuItem *voice_enable;
+
+#define ALTOS_VOICE_PATH "/apps/aoview/voice"
+
+static void
+aoview_voice_enable(GtkWidget *widget, gpointer data)
+{
+ gboolean enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ GError *error;
+ GConfClient *gconf_client;
+
+ if (enabled) {
+ aoview_voice_open();
+ aoview_voice_speak("enable voice\n");
+ } else {
+ aoview_voice_speak("disable voice\n");
+ aoview_voice_close();
+ }
+ gconf_client = gconf_client_get_default();
+ gconf_client_set_bool(gconf_client,
+ ALTOS_VOICE_PATH,
+ enabled,
+ &error);
+}
+
+void
+aoview_voice_init(GladeXML *xml)
+{
+ gboolean enabled;
+ GConfClient *gconf_client;
+
+ voice_enable = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "voice_enable"));
+ assert(voice_enable);
+
+ gconf_client = gconf_client_get_default();
+ enabled = TRUE;
+ if (gconf_client)
+ {
+ GError *error;
+
+ error = NULL;
+ enabled = gconf_client_get_bool(gconf_client,
+ ALTOS_VOICE_PATH,
+ &error);
+ if (error)
+ enabled = TRUE;
+ }
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(voice_enable), enabled);
+ if (enabled)
+ aoview_voice_open();
+
+ g_signal_connect(G_OBJECT(voice_enable), "toggled",
+ G_CALLBACK(aoview_voice_enable),
+ voice_enable);
+}
--- /dev/null
+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
ccdbg-state.c \
cc-usb.c \
cc-usb.h \
+ cc.h \
+ cc-usbdev.c \
+ cc-util.c \
cc-bitbang.c \
cc-bitbang.h \
cp-usb-async.c \
--- /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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "cc.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *
+load_string(char *dir, char *file)
+{
+ char *full = cc_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 int
+dir_filter_tty_colon(const struct dirent *d)
+{
+ return strncmp(d->d_name, "tty:", 4) == 0;
+}
+
+static int
+dir_filter_tty(const struct dirent *d)
+{
+ return strncmp(d->d_name, "tty", 3) == 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;
+ char *tty_dir;
+ int ntty;
+ char *tty;
+
+ base = cc_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 = cc_fullname(sys, endpoint_base);
+
+ /* Check for tty:ttyACMx style names
+ */
+ ntty = scandir(endpoint_full, &namelist,
+ dir_filter_tty_colon,
+ alphasort);
+ if (ntty > 0) {
+ free(endpoint_full);
+ tty = cc_fullname("/dev", namelist[0]->d_name + 4);
+ free(namelist);
+ return tty;
+ }
+
+ /* Check for tty/ttyACMx style names
+ */
+ tty_dir = cc_fullname(endpoint_full, "tty");
+ free(endpoint_full);
+ ntty = scandir(tty_dir, &namelist,
+ dir_filter_tty,
+ alphasort);
+ free (tty_dir);
+ if (ntty > 0) {
+ tty = cc_fullname("/dev", namelist[0]->d_name);
+ free(namelist);
+ return tty;
+ }
+ }
+ }
+ return NULL;
+}
+
+static struct cc_usbdev *
+usb_scan_device(char *sys)
+{
+ struct cc_usbdev *usbdev;
+
+ usbdev = calloc(1, sizeof (struct cc_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;
+}
+
+static void
+usbdev_free(struct cc_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;
+ if (c == '.' && n != d->d_name + 1)
+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+struct cc_usbdevs *
+cc_usbdevs_scan(void)
+{
+ int e;
+ struct dirent **ents;
+ char *dir;
+ struct cc_usbdev *dev;
+ struct cc_usbdevs *devs;
+ int n;
+
+ devs = calloc(1, sizeof (struct cc_usbdevs));
+ if (!devs)
+ return NULL;
+
+ n = scandir (USB_DEVICES, &ents,
+ dir_filter_dev,
+ alphasort);
+ if (!n)
+ return 0;
+ for (e = 0; e < n; e++) {
+ dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
+ dev = usb_scan_device(dir);
+ free(dir);
+ if (dev->idVendor == 0xfffe && dev->tty) {
+ if (devs->dev)
+ devs->dev = realloc(devs->dev,
+ devs->ndev + 1 * sizeof (struct usbdev *));
+ else
+ devs->dev = malloc (sizeof (struct usbdev *));
+ devs->dev[devs->ndev++] = dev;
+ }
+ }
+ free(ents);
+ return devs;
+}
+
+void
+cc_usbdevs_free(struct cc_usbdevs *usbdevs)
+{
+ int i;
+
+ if (!usbdevs)
+ return;
+ for (i = 0; i < usbdevs->ndev; i++)
+ usbdev_free(usbdevs->dev[i]);
+ free(usbdevs);
+}
--- /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 "cc.h"
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+char *
+cc_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 *
+cc_basename(char *file)
+{
+ char *b;
+
+ b = strrchr(file, '/');
+ if (!b)
+ return file;
+ return b + 1;
+}
+
+int
+cc_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;
+}
--- /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.
+ */
+
+#ifndef _CC_H_
+#define _CC_H_
+
+char *
+cc_fullname (char *dir, char *file);
+
+char *
+cc_basename(char *file);
+
+int
+cc_mkdir(char *dir);
+
+struct cc_usbdev {
+ char *sys;
+ char *tty;
+ char *manufacturer;
+ char *product;
+ char *serial;
+ int idProduct;
+ int idVendor;
+};
+
+struct cc_usbdevs {
+ struct cc_usbdev **dev;
+ int ndev;
+};
+
+void
+cc_usbdevs_free(struct cc_usbdevs *usbdevs);
+
+struct cc_usbdevs *
+cc_usbdevs_scan(void);
+
+#endif /* _CC_H_ */
+++ /dev/null
-*.o
-aoview
-aoview_glade.h
-aoview_flite
+++ /dev/null
-VERSION=$(shell git describe)
-
-AM_CFLAGS=$(GNOME_CFLAGS) $(ALSA_CFLAGS) -I$(top_srcdir)/src -DAOVIEW_VERSION=\"$(VERSION)\" @FLITE_INCS@
-
-bin_PROGRAMS=ao-view
-
-ao_view_LDADD=$(GNOME_LIBS) $(FLITE_LIBS) $(ALSA_LIBS)
-
-ao_view_SOURCES = \
- aoview_main.c \
- aoview_dev.c \
- aoview_dev_dialog.c \
- aoview_serial.c \
- aoview_monitor.c \
- aoview_state.c \
- aoview_convert.c \
- aoview_log.c \
- aoview_table.c \
- aoview_util.c \
- aoview_file.c \
- aoview_eeprom.c \
- aoview_voice.c \
- aoview_replay.c \
- aoview_label.c \
- aoview_flite.c \
- aoview.h
-
-BUILT_SOURCES = aoview_glade.h
-
-CLEANFILES = aoview_glade.h
-
-man_MANS=ao-view.1
-
-aoview_glade.h: aoview.glade
- sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/"/' $< > $@
+++ /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; either version 2 of the License, or
-.\" (at your option) any later version.
-.\"
-.\" 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.
-.\"
-.\"
-.TH AO-VIEW 1 "ao-view" ""
-.SH NAME
-ao-view \- Rocket flight monitor
-.SH SYNOPSIS
-.B "ao-view"
-[\--tty \fItty-device\fP]
-.SH DESCRIPTION
-.I ao-view
-connects to a TeleDongle or TeleMetrum device through a USB serial device.
-It provides a user interface to monitor, record and review rocket flight data.
-.SH OPTIONS
-The usual Gtk+ command line options can be used, along with
-.IP "\--tty"
-This selects a target device to connect at startup time to.
-The target device may also be selected through the user interface.
-.SH USAGE
-When connected to a TeleDongle device, ao-view turns on the radio
-receiver and listens for telemetry packets. It displays the received
-telemetry data, and reports flight status via voice synthesis. All
-received telemetry information is recorded to a file.
-.P
-When connected to a TeleMetrum device, ao-view downloads the eeprom
-data and stores it in a file.
-.SH FILES
-All data log files are recorded into a user-specified directory
-(default ~/AltOS). Files are named using the current date, the serial
-number of the reporting device, the flight number recorded in the data
-and either '.telem' for telemetry data or '.eeprom' for eeprom data.
-.SH "SEE ALSO"
-ao-load(1), ao-eeprom(1)
-.SH AUTHOR
-Keith Packard
+++ /dev/null
-<?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">900</property>
- <property name="height_request">700</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>
- <child>
- <widget class="GtkSeparatorMenuItem" id="seperator">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="ao_savelog">
- <property name="label" translatable="yes">_Save EEPROM data</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"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image5">
- <property name="visible">True</property>
- <property name="stock">gtk-save</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="ao_replay">
- <property name="label" translatable="yes">_Replay</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="ao_replay_dialog" after="yes"/>
- <signal name="activate" handler="gtk_widget_show" object="ao_replay_dialog"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image6">
- <property name="visible">True</property>
- <property name="stock">gtk-media-play</property>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </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="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="file_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="file_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="menuitem6">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Voice</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="menu6">
- <property name="visible">True</property>
- <child>
- <widget class="GtkCheckMenuItem" id="voice_enable">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Enable _Voice</property>
- <property name="use_underline">True</property>
- <property name="active">True</property>
- </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>
- <signal name="activate" handler="gtk_widget_show" object="about_dialog" after="yes"/>
- </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">2</property>
- <property name="n_columns">4</property>
- <property name="row_spacing">3</property>
- <property name="homogeneous">True</property>
- <child>
- <widget class="GtkLabel" id="height_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Height (m)</property>
- <property name="justify">center</property>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="state_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">State</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="rssi_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">RSSI (dBm)</property>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="height_value">
- <property name="visible">True</property>
- <property name="ypad">2</property>
- <property name="label" translatable="yes">0</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="state_value">
- <property name="visible">True</property>
- <property name="ypad">2</property>
- <property name="label" translatable="yes">pad</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="rssi_value">
- <property name="visible">True</property>
- <property name="ypad">2</property>
- <property name="label" translatable="yes">-50</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="speed_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Speed (m/s)</property>
- </widget>
- <packing>
- <property name="left_attach">3</property>
- <property name="right_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="speed_value">
- <property name="visible">True</property>
- <property name="label" translatable="yes">0</property>
- <property name="selectable">True</property>
- </widget>
- <packing>
- <property name="left_attach">3</property>
- <property name="right_attach">4</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <child>
- <widget class="GtkTreeView" id="dataview_0">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="show_expanders">False</property>
- <property name="enable_grid_lines">both</property>
- </widget>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkTreeView" id="dataview_1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="show_expanders">False</property>
- <property name="enable_grid_lines">both</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkTreeView" id="dataview_2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="show_expanders">False</property>
- <property name="enable_grid_lines">both</property>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">2</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="cancel_button">
- <property name="label">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">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="connect_button">
- <property name="label">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="file_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="file_configure_cancel">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="gtk_widget_hide" object="file_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="file_configure_ok">
- <property name="label">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>
- <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="GtkMessageDialog" id="file_fail_dialog">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Failed to create log</property>
- <property name="type_hint">normal</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="transient_for">aoview</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>
- <widget class="GtkMessageDialog" id="dev_open_fail_dialog">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Failed to open device</property>
- <property name="type_hint">normal</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="transient_for">aoview</property>
- <property name="message_type">error</property>
- <property name="buttons">close</property>
- <property name="text">Cannot open device</property>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox6">
- <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_area6">
- <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>
- <widget class="GtkAboutDialog" id="about_dialog">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">About AoView</property>
- <property name="resizable">False</property>
- <property name="type_hint">normal</property>
- <property name="transient_for">aoview</property>
- <property name="has_separator">False</property>
- <property name="program_name">AoView</property>
- <property name="copyright" translatable="yes">Copyright © 2009 Keith Packard</property>
- <property name="comments" translatable="yes">AltOS data capture and display.</property>
- <property name="website">http://altusmetrum.org</property>
- <property name="license" translatable="yes">AoView 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.
-
-AoView 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 AoView; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property>
- <property name="authors">Keith Packard <keithp@keithp.com></property>
- <property name="wrap_license">True</property>
- <signal name="close" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
- <signal name="response" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox7">
- <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_area7">
- <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>
- <widget class="GtkMessageDialog" id="ao_save_done">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">EEPROM save complete</property>
- <property name="type_hint">normal</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="transient_for">aoview</property>
- <property name="buttons">close</property>
- <property name="text">Saving EEPROM data as</property>
- <property name="secondary_text"><filename></property>
- <signal name="close" handler="gtk_widget_hide" object="ao_save_done"/>
- <signal name="response" handler="gtk_widget_hide" object="ao_save_done"/>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox11">
- <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_area11">
- <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>
- <widget class="GtkFileChooserDialog" id="ao_replay_dialog">
- <property name="border_width">5</property>
- <property name="destroy_with_parent">True</property>
- <property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="transient_for">aoview</property>
- <property name="has_separator">False</property>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox10">
- <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_area10">
- <property name="visible">True</property>
- <property name="layout_style">end</property>
- <child>
- <widget class="GtkButton" id="ao_replay_cancel">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="gtk_widget_hide" object="ao_replay_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="ao_replay_ok">
- <property name="label">gtk-ok</property>
- <property name="visible">True</property>
- <property name="can_focus">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>
-</glade-interface>
+++ /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.
- */
-
-#ifndef _AOVIEW_H_
-#define _AOVIEW_H_
-
-#define _GNU_SOURCE
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#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>
-#include <math.h>
-
-#include <gtk/gtk.h>
-#include <glade/glade.h>
-#include <gconf/gconf-client.h>
-
-struct usbdev {
- char *sys;
- char *tty;
- char *manufacturer;
- char *product;
- char *serial;
- int idProduct;
- int idVendor;
-};
-
-struct aogps_time {
- int hour;
- int minute;
- int second;
-};
-
-struct aogps {
- int nsat;
- int gps_locked;
- int gps_connected;
- struct aogps_time gps_time;
- double lat; /* degrees (+N -S) */
- double lon; /* degrees (+E -W) */
- int alt; /* m */
-
- int gps_extended; /* has extra data */
- double ground_speed; /* m/s */
- int course; /* degrees */
- double climb_rate; /* m/s */
- double hdop; /* unitless? */
- int h_error; /* m */
- int v_error; /* m */
-};
-
-#define SIRF_SAT_STATE_ACQUIRED (1 << 0)
-#define SIRF_SAT_STATE_CARRIER_PHASE_VALID (1 << 1)
-#define SIRF_SAT_BIT_SYNC_COMPLETE (1 << 2)
-#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3)
-#define SIRF_SAT_CARRIER_PULLIN_COMPLETE (1 << 4)
-#define SIRF_SAT_CODE_LOCKED (1 << 5)
-#define SIRF_SAT_ACQUISITION_FAILED (1 << 6)
-#define SIRF_SAT_EPHEMERIS_AVAILABLE (1 << 7)
-
-struct aogps_sat {
- int svid;
- int state;
- int c_n0;
-};
-
-struct aogps_tracking {
- int channels;
- struct aogps_sat sats[12];
-};
-
-struct aodata {
- 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 flight_accel;
- int ground_accel;
- int flight_vel;
- int flight_pres;
- int ground_pres;
- struct aogps gps;
- struct aogps_tracking gps_tracking;
-};
-
-struct aostate {
- struct aodata data;
-
- /* derived data */
-
- struct aodata prev_data;
-
- double report_time;
-
- gboolean ascent; /* going up? */
-
- int ground_altitude;
- int height;
- double speed;
- double acceleration;
- double battery;
- double temperature;
- double main_sense;
- double drogue_sense;
- double baro_speed;
-
- int max_height;
- double max_acceleration;
- double max_speed;
-
- struct aogps gps;
- struct aogps_tracking gps_tracking;
-
- int gps_valid;
- double pad_lat;
- double pad_lon;
- double pad_alt;
- double pad_lat_total;
- double pad_lon_total;
- double pad_alt_total;
- int npad;
- int prev_npad;
-
- double distance;
- double bearing;
- int gps_height;
-
- int speak_tick;
- int speak_altitude;
-};
-
-extern struct aostate aostate;
-
-/* GPS is 'stable' when we've seen at least this many samples */
-#define MIN_PAD_SAMPLES 10
-
-void
-aoview_monitor_disconnect(void);
-
-gboolean
-aoview_monitor_connect(char *tty);
-
-gboolean
-aoview_monitor_parse(const char *line);
-
-void
-aoview_monitor_reset(void);
-
-struct aoview_serial *
-aoview_serial_open(const char *tty);
-
-void
-aoview_serial_close(struct aoview_serial *serial);
-
-typedef void (*aoview_serial_callback)(gpointer user_data, struct aoview_serial *serial, gint revents);
-
-void
-aoview_serial_set_callback(struct aoview_serial *serial,
- aoview_serial_callback func);
-
-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 aodata *data);
-
-void
-aoview_state_new(void);
-
-void
-aoview_state_init(GladeXML *xml);
-
-int16_t
-aoview_pres_to_altitude(int16_t pres);
-
-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_log_new(void);
-
-void
-aoview_table_start(void);
-
-void
-aoview_table_add_row(int column, char *label, char *format, ...);
-
-void
-aoview_table_finish(void);
-
-void
-aoview_table_init(GladeXML *xml);
-
-void
-aoview_table_clear(void);
-
-struct aoview_file;
-
-extern char *aoview_file_dir;
-
-void
-aoview_file_finish(struct aoview_file *file);
-
-gboolean
-aoview_file_start(struct aoview_file *file);
-
-const char *
-aoview_file_name(struct aoview_file *file);
-
-void
-aoview_file_set_serial(struct aoview_file *file, int serial);
-
-int
-aoview_file_get_serial(struct aoview_file *file);
-
-void
-aoview_file_printf(struct aoview_file *file, char *format, ...);
-
-void
-aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap);
-
-struct aoview_file *
-aoview_file_new(char *ext);
-
-void
-aoview_file_destroy(struct aoview_file *file);
-
-void
-aoview_file_init(GladeXML *xml);
-
-/* aoview_eeprom.c */
-
-gboolean
-aoview_eeprom_save(const char *device);
-
-void
-aoview_eeprom_init(GladeXML *xml);
-
-/* aoview_voice.c */
-void aoview_voice_open(void);
-
-void aoview_voice_close(void);
-
-void aoview_voice_speak(char *format, ...);
-
-/* aoview_label.c */
-
-void aoview_label_init(GladeXML *xml);
-
-void
-aoview_label_show(struct aostate *state);
-
-/* aoview_flite.c */
-
-FILE *
-aoview_flite_start(void);
-
-void
-aoview_flite_stop(void);
-
-/* aoview_main.c */
-
-extern char *aoview_tty;
-
-#endif /* _AOVIEW_H_ */
+++ /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 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;
-}
+++ /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"
-#include <ctype.h>
-#include <dirent.h>
-
-static char *
-load_string(char *dir, char *file)
-{
- char *full = aoview_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 int
-dir_filter_tty_colon(const struct dirent *d)
-{
- return strncmp(d->d_name, "tty:", 4) == 0;
-}
-
-static int
-dir_filter_tty(const struct dirent *d)
-{
- return strncmp(d->d_name, "tty", 3) == 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;
- char *tty_dir;
- int ntty;
- char *tty;
-
- 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 = aoview_fullname(sys, endpoint_base);
-
- /* Check for tty:ttyACMx style names
- */
- ntty = scandir(endpoint_full, &namelist,
- dir_filter_tty_colon,
- alphasort);
- if (ntty > 0) {
- free(endpoint_full);
- tty = aoview_fullname("/dev", namelist[0]->d_name + 4);
- free(namelist);
- return tty;
- }
-
- /* Check for tty/ttyACMx style names
- */
- tty_dir = aoview_fullname(endpoint_full, "tty");
- free(endpoint_full);
- ntty = scandir(tty_dir, &namelist,
- dir_filter_tty,
- alphasort);
- free (tty_dir);
- if (ntty > 0) {
- tty = aoview_fullname("/dev", namelist[0]->d_name);
- 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;
- if (c == '.' && n != d->d_name + 1)
- 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 = aoview_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;
-}
+++ /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 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 GtkMessageDialog *dev_open_fail_dialog;
-
-static void
-aoview_dev_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(dev_open_fail_dialog,
- "\"%s\"", utf8_file);
- if (utf8_file != name)
- g_free(utf8_file);
- gtk_dialog_run(GTK_DIALOG(dev_open_fail_dialog));
- gtk_widget_hide(GTK_WIDGET(dev_open_fail_dialog));
-}
-
-gboolean dialog_save_log;
-
-static void
-aoview_dev_selected(GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- gchar *string;
- gtk_tree_model_get(model, iter,
- 2, &string,
- -1);
- if (dialog_save_log) {
- dialog_save_log = FALSE;
- if (!aoview_eeprom_save(string))
- aoview_dev_open_failed(string);
- } else {
- if (!aoview_monitor_connect(string))
- aoview_dev_open_failed(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();
-}
-
-static void
-aoview_dev_savelog(GtkWidget *widget, gpointer data)
-{
- dialog_save_log = TRUE;
- gtk_widget_show(dialog);
-}
-
-#define _(a) a
-
-void
-aoview_dev_dialog_init(GladeXML *xml)
-{
- GtkTreeView *dev_list;
- GtkWidget *connect_button;
- GtkTreeSelection *dev_selection;
- GtkWidget *ao_disconnect;
- GtkWidget *ao_savelog;
-
- 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);
-
- 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);
-
- 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);
-
- ao_savelog = glade_xml_get_widget(xml, "ao_savelog");
- assert(ao_savelog);
-
- g_signal_connect(G_OBJECT(ao_savelog), "activate",
- G_CALLBACK(aoview_dev_savelog),
- dialog);
- dev_open_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "dev_open_fail_dialog"));
- assert(dev_open_fail_dialog);
-}
+++ /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 EEPROM_LEN 1024
-
-static struct aoview_file *eeprom_file;
-static char eeprom_line[EEPROM_LEN + 1];
-static int eeprom_pos;
-static GtkMessageDialog *eeprom_save_done;
-static GtkWidget *eeprom_save_close;
-static gboolean eeprom_save_shown;
-
-static void
-aoview_eeprom_disconnect(struct aoview_serial *serial)
-{
- aoview_file_finish(eeprom_file);
-}
-
-static void
-aoview_eeprom_done(struct aoview_serial *serial)
-{
- gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
- "EEPROM data saved");
- gtk_message_dialog_set_markup(eeprom_save_done,
- "<b>EEPROM data saved as</b>");
- if (!eeprom_save_shown)
- gtk_widget_show(GTK_WIDGET(eeprom_save_done));
- eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
- if (eeprom_save_close)
- gtk_widget_set_sensitive(eeprom_save_close, TRUE);
- aoview_eeprom_disconnect(serial);
-}
-
-static gboolean
-aoview_eeprom_parse(struct aoview_serial *serial,
- char *line)
-{
- char cmd;
- int tick;
- int a;
- int b;
- int serial_number;
- const char *name;
- char *utf8_name;
-
- if (!strcmp(line, "end")) {
- aoview_eeprom_done(serial);
- return FALSE;
- }
- if (sscanf(line, "serial-number %u", &serial_number) == 1) {
- aoview_file_set_serial(eeprom_file, serial_number);
- } else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) {
- aoview_file_printf(eeprom_file, "%s\n", line);
- if (cmd == 'S' && a == 8) {
- aoview_eeprom_done(serial);
- return FALSE;
- }
-
- if (!eeprom_save_shown)
- {
- name = aoview_file_name(eeprom_file);
- if (name) {
- utf8_name = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
- if (!utf8_name)
- utf8_name = (char *) name;
- gtk_widget_set_sensitive(eeprom_save_close, FALSE);
- gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
- "Saving EEPROM data");
- gtk_message_dialog_set_markup(eeprom_save_done,
- "<b>Saving EEPROM data as</b>");
- gtk_message_dialog_format_secondary_text(eeprom_save_done, "%s",
- utf8_name);
- if (utf8_name != name)
- g_free(utf8_name);
- gtk_container_check_resize(GTK_CONTAINER(eeprom_save_done));
- gtk_widget_show(GTK_WIDGET(eeprom_save_done));
- eeprom_save_shown = TRUE;
- eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
- if (eeprom_save_close)
- gtk_widget_set_sensitive(eeprom_save_close, FALSE);
- }
- }
- }
- return TRUE;
-}
-
-static void
-aoview_eeprom_callback(gpointer user_data,
- struct aoview_serial *serial,
- gint revents)
-{
- int c;
-
- if (revents & (G_IO_HUP|G_IO_ERR)) {
- aoview_eeprom_disconnect(serial);
- return;
- }
- if (revents & G_IO_IN) {
- for (;;) {
- c = aoview_serial_getc(serial);
- if (c == -1)
- break;
- if (c == '\r')
- continue;
- if (c == '\n') {
- eeprom_line[eeprom_pos] = '\0';
- if (eeprom_pos)
- if (!aoview_eeprom_parse(serial, eeprom_line))
- break;
- eeprom_pos = 0;
- } else if (eeprom_pos < EEPROM_LEN)
- eeprom_line[eeprom_pos++] = c;
- }
- }
-}
-
-gboolean
-aoview_eeprom_save(const char *device)
-{
- struct aoview_serial *serial;
-
- gtk_widget_hide(GTK_WIDGET(eeprom_save_done));
- eeprom_save_shown = FALSE;
- serial = aoview_serial_open(device);
- if (!serial)
- return FALSE;
- aoview_serial_set_callback(serial, aoview_eeprom_callback);
- aoview_serial_printf(serial, "v\nl\n");
- return TRUE;
-}
-
-void
-aoview_eeprom_init(GladeXML *xml)
-{
- eeprom_file = aoview_file_new("eeprom");
- assert(eeprom_file);
-
- eeprom_save_done = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "ao_save_done"));
- assert(eeprom_save_done);
-
-}
+++ /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_file_dir;
-
-#define ALTOS_DIR_PATH "/apps/aoview/log_dir"
-#define DEFAULT_DIR "AltOS"
-
-struct aoview_file {
- char *ext;
- FILE *file;
- char *name;
- int failed;
- int serial;
- int sequence;
-};
-
-static void
-aoview_file_save_conf(void)
-{
- GConfClient *gconf_client;
-
- gconf_client = gconf_client_get_default();
- if (gconf_client)
- {
- gconf_client_set_string(gconf_client,
- ALTOS_DIR_PATH,
- aoview_file_dir,
- NULL);
- g_object_unref(G_OBJECT(gconf_client));
- }
-}
-
-static void
-aoview_file_configure(GtkWidget *widget, gpointer data)
-{
- GtkFileChooser *chooser = data;
- aoview_file_dir = gtk_file_chooser_get_filename(chooser);
- aoview_file_save_conf();
- gtk_widget_hide(GTK_WIDGET(chooser));
-}
-
-void
-aoview_file_finish(struct aoview_file *file)
-{
- if (file->file) {
- fclose(file->file);
- file->file = NULL;
- free(file->name);
- file->name = NULL;
- }
- file->failed = 0;
-}
-
-const char *
-aoview_file_name(struct aoview_file *file)
-{
- return file->name;
-}
-
-static GtkMessageDialog *file_fail_dialog;
-
-static void
-aoview_file_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(file_fail_dialog,
- "\"%s\"", utf8_file);
- if (utf8_file != name)
- g_free(utf8_file);
- gtk_widget_show(GTK_WIDGET(file_fail_dialog));
-}
-
-gboolean
-aoview_file_start(struct aoview_file *file)
-{
- char base[50];
- struct tm tm;
- time_t now;
- char *full;
- int r;
-
- if (file->file)
- return TRUE;
-
- if (file->failed)
- return FALSE;
-
- now = time(NULL);
- (void) localtime_r(&now, &tm);
- aoview_mkdir(aoview_file_dir);
- for (;;) {
- snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d.%s",
- tm.tm_year + 1900,
- tm.tm_mon + 1,
- tm.tm_mday,
- file->serial,
- file->sequence,
- file->ext);
- full = aoview_fullname(aoview_file_dir, base);
- r = access(full, F_OK);
- if (r < 0) {
- file->file = fopen(full, "w");
- if (!file->file) {
- aoview_file_open_failed(full);
- free(full);
- file->failed = 1;
- return FALSE;
- } else {
- setlinebuf(file->file);
- file->name = full;
- return TRUE;
- }
- }
- free(full);
- file->sequence++;
- }
-}
-
-void
-aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap)
-{
- if (!aoview_file_start(file))
- return;
- vfprintf(file->file, format, ap);
-}
-
-void
-aoview_file_printf(struct aoview_file *file, char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- aoview_file_vprintf(file, format, ap);
- va_end(ap);
-}
-
-struct aoview_file *
-aoview_file_new(char *ext)
-{
- struct aoview_file *file;
-
- file = calloc (1, sizeof (struct aoview_file));
- if (!file)
- return NULL;
- file->ext = strdup(ext);
- if (!file->ext) {
- free(file);
- return NULL;
- }
- return file;
-}
-
-void
-aoview_file_destroy(struct aoview_file *file)
-{
- if (file->file)
- fclose(file->file);
- if (file->name)
- free(file->name);
- free(file->ext);
- free(file);
-}
-
-void
-aoview_file_set_serial(struct aoview_file *file, int serial)
-{
- if (serial != file->serial)
- aoview_file_finish(file);
- file->serial = serial;
-}
-
-int
-aoview_file_get_serial(struct aoview_file *file)
-{
- return file->serial;
-}
-
-void
-aoview_file_init(GladeXML *xml)
-{
- GConfClient *gconf_client;
- char *file_dir = NULL;
- GtkFileChooser *file_chooser_dialog;
- GtkWidget *file_configure_ok;
-
- g_type_init();
- gconf_client = gconf_client_get_default();
- if (gconf_client)
- {
- file_dir = gconf_client_get_string(gconf_client,
- ALTOS_DIR_PATH,
- NULL);
- g_object_unref(G_OBJECT(gconf_client));
- }
- if (!file_dir) {
- aoview_file_dir = aoview_fullname(getenv("HOME"), DEFAULT_DIR);
- aoview_file_save_conf();
- } else {
- aoview_file_dir = strdup(file_dir);
- }
-
- file_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "file_chooser_dialog"));
- assert(file_chooser_dialog);
- gtk_file_chooser_set_filename(file_chooser_dialog, aoview_file_dir);
-
- file_configure_ok = glade_xml_get_widget(xml, "file_configure_ok");
- assert(file_configure_ok);
-
- g_signal_connect(G_OBJECT(file_configure_ok), "clicked",
- G_CALLBACK(aoview_file_configure),
- file_chooser_dialog);
-
-
- file_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "file_fail_dialog"));
- assert(file_fail_dialog);
-}
+++ /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 <stdio.h>
-#include <flite/flite.h>
-#include "aoview.h"
-#include <alsa/asoundlib.h>
-
-cst_voice *register_cmu_us_kal();
-static cst_voice *voice;
-
-static FILE *pipe_write;
-static GThread *aoview_flite_thread;
-
-static snd_pcm_t *alsa_handle;
-
-gpointer
-aoview_flite_task(gpointer data)
-{
- FILE *input = data;
- char line[1024];
- cst_wave *wave;
- int rate;
- int channels;
- int err;
- char *samples;
- int num_samples;
-
- err = snd_pcm_open(&alsa_handle, "default",
- SND_PCM_STREAM_PLAYBACK, 0);
- if (err >= 0)
- {
- if (err < 0) {
- snd_pcm_close(alsa_handle);
- alsa_handle = 0;
- }
- }
- rate = 0;
- channels = 0;
- while (fgets(line, sizeof (line) - 1, input) != NULL) {
- if (!alsa_handle)
- continue;
- wave = flite_text_to_wave(line, voice);
- if (wave->sample_rate != rate ||
- wave->num_channels != channels)
- {
- rate = wave->sample_rate;
- channels = wave->num_channels;
- err = snd_pcm_set_params(alsa_handle,
- SND_PCM_FORMAT_S16,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- channels,
- rate,
- 1,
- 100000);
- if (err < 0)
- fprintf(stderr, "alsa set_params error %s\n",
- strerror(-err));
- }
- err = snd_pcm_prepare(alsa_handle);
- if (err < 0)
- fprintf(stderr, "alsa pcm_prepare error %s\n",
- strerror(-err));
- samples = (char *) wave->samples;
- num_samples = wave->num_samples;
- while (num_samples > 0) {
- err = snd_pcm_writei(alsa_handle,
- samples, num_samples);
- if (err <= 0) {
- fprintf(stderr, "alsa write error %s\n",
- strerror(-err));
- break;
- }
- num_samples -= err;
- samples += err * 2 * channels;
- }
- snd_pcm_drain(alsa_handle);
- delete_wave(wave);
- }
- snd_pcm_close(alsa_handle);
- alsa_handle = 0;
- return NULL;
-}
-
-void
-aoview_flite_stop(void)
-{
- int status;
- if (pipe_write) {
- fclose(pipe_write);
- pipe_write = NULL;
- }
- if (aoview_flite_thread) {
- g_thread_join(aoview_flite_thread);
- aoview_flite_thread = NULL;
- }
-}
-
-FILE *
-aoview_flite_start(void)
-{
- static once;
- int p[2];
- GError *error;
- FILE *pipe_read;
-
- if (!once) {
- flite_init();
- voice = register_cmu_us_kal();
- if (!voice) {
- perror("register voice");
- exit(1);
- }
- }
- aoview_flite_stop();
- pipe(p);
- pipe_read = fdopen(p[0], "r");
- pipe_write = fdopen(p[1], "w");
- g_thread_create(aoview_flite_task, pipe_read, TRUE, &error);
- return pipe_write;
-}
+++ /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 struct {
- char *name;
- char *initial_value;
- GtkLabel *widget;
-} label_widgets[] = {
- { "height_label", "Height (m)", NULL },
- { "state_label", "State", NULL },
- { "rssi_label", "RSSI (dBm)", NULL },
- { "speed_label", "Speed (m/s)", NULL },
- { "height_value", "0", NULL },
- { "state_value", "pad", NULL },
- { "rssi_value", "-50", NULL },
- { "speed_value", "0", NULL },
-};
-
-static void
-aoview_label_assign(GtkLabel *widget, char *value)
-{
- char *markup;
-
- markup = g_markup_printf_escaped("<span font_weight=\"bold\" size=\"xx-large\">%s</span>", value);
- gtk_label_set_markup(widget, markup);
- g_free(markup);
-}
-
-void
-aoview_label_show(struct aostate *state)
-{
- char line[1024];
- sprintf(line, "%d", state->height);
- aoview_label_assign(label_widgets[4].widget, line);
-
- aoview_label_assign(label_widgets[5].widget, state->data.state);
-
- sprintf(line, "%d", state->data.rssi);
- aoview_label_assign(label_widgets[6].widget, line);
-
- if (state->ascent)
- sprintf(line, "%6.0f", fabs(state->speed));
- else
- sprintf(line, "%6.0f", fabs(state->baro_speed));
- aoview_label_assign(label_widgets[7].widget, line);
-}
-
-void
-aoview_label_init(GladeXML *xml)
-{
- int i;
- for (i = 0; i < sizeof(label_widgets)/sizeof(label_widgets[0]); i++) {
- label_widgets[i].widget = GTK_LABEL(glade_xml_get_widget(xml, label_widgets[i].name));
- aoview_label_assign(label_widgets[i].widget, label_widgets[i].initial_value);
- assert(label_widgets[i].widget);
- }
-}
+++ /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 struct aoview_file *aoview_log;
-
-void
-aoview_log_new(void)
-{
- aoview_file_finish(aoview_log);
- aoview_state_new();
-}
-
-void
-aoview_log_set_serial(int serial)
-{
- aoview_file_set_serial(aoview_log, serial);
-}
-
-int
-aoview_log_get_serial(void)
-{
- return aoview_file_get_serial(aoview_log);
-}
-
-void
-aoview_log_printf(char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- aoview_file_vprintf(aoview_log, format, ap);
- va_end(ap);
-}
-
-static void
-aoview_log_new_item(GtkWidget *widget, gpointer data)
-{
- aoview_file_finish(aoview_log);
-}
-
-void
-aoview_log_init(GladeXML *xml)
-{
- GtkWidget *log_new;
-
- aoview_log = aoview_file_new("telem");
- assert(aoview_log);
-
- log_new = glade_xml_get_widget(xml, "log_new");
- assert(log_new);
- g_signal_connect(G_OBJECT(log_new), "activate",
- G_CALLBACK(aoview_log_new_item),
- NULL);
-}
+++ /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 const char aoview_glade[] = {
-#include "aoview_glade.h"
-};
-
-static void usage(void) {
- printf("aoview [--device|-d device_file]");
- exit(1);
-}
-
-static void destroy_event(GtkWidget *widget, gpointer data)
-{
- gtk_main_quit();
-}
-
-extern int _Xdebug;
-char *aoview_tty = NULL;
-
-int main(int argc, char **argv)
-{
- GladeXML *xml = NULL;
- GtkWidget *mainwindow;
- GtkAboutDialog *about_dialog;
-
- static struct option long_options[] = {
- { "tty", 1, 0, 'T'},
- { "sync", 0, 0, 's'},
- { 0, 0, 0, 0 }
- };
- for (;;) {
- int c, temp;
-
- c = getopt_long_only(argc, argv, "sT:", long_options, &temp);
- if (c == -1)
- break;
-
- switch (c) {
- case 'T':
- aoview_tty = optarg;
- break;
- case 's':
- _Xdebug = 1;
- break;
- default:
- usage();
- }
- }
-
- g_thread_init(NULL);
- gtk_init(&argc, &argv);
- glade_init();
-
- xml = glade_xml_new_from_buffer(aoview_glade, sizeof (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");
- assert(mainwindow);
-
- g_signal_connect (G_OBJECT(mainwindow), "destroy",
- G_CALLBACK(destroy_event), NULL);
-
- about_dialog = GTK_ABOUT_DIALOG(glade_xml_get_widget(xml, "about_dialog"));
- assert(about_dialog);
- gtk_about_dialog_set_version(about_dialog, AOVIEW_VERSION);
-
- aoview_voice_init(xml);
-
- aoview_dev_dialog_init(xml);
-
- aoview_state_init(xml);
-
- aoview_file_init(xml);
-
- aoview_log_init(xml);
-
- aoview_table_init(xml);
-
- aoview_eeprom_init(xml);
-
- aoview_replay_init(xml);
-
- aoview_label_init(xml);
-
- if (aoview_tty) {
- if (!aoview_monitor_connect(aoview_tty)) {
- perror(aoview_tty);
- exit(1);
- }
- }
- aoview_voice_speak("rocket flight monitor ready\n");
-
- gtk_main();
-
- return 0;
-}
+++ /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 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;
- }
- aoview_log_new();
-}
-
-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_hex(int *target, char *source)
-{
- *target = strtol(source, NULL, 16);
-}
-
-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;
-}
-
-#define PARSE_MAX_WORDS 256
-
-gboolean
-aoview_monitor_parse(const char *input_line)
-{
- char *saveptr;
- char *words[PARSE_MAX_WORDS];
- int nword;
- char line_buf[8192], *line;
- struct aodata data;
- int tracking_pos;
-
- /* avoid smashing our input parameter */
- strncpy (line_buf, input_line, sizeof (line_buf)-1);
- line_buf[sizeof(line_buf) - 1] = '\0';
- line = line_buf;
- for (nword = 0; nword < PARSE_MAX_WORDS; nword++) {
- words[nword] = strtok_r(line, " \t\n", &saveptr);
- line = NULL;
- if (words[nword] == NULL)
- break;
- }
- if (nword < 36)
- return FALSE;
- if (strcmp(words[0], "CALL") != 0)
- return FALSE;
- aoview_parse_string(data.callsign, sizeof (data.callsign), words[1]);
- aoview_parse_int(&data.serial, words[3]);
-
- aoview_parse_int(&data.rssi, words[5]);
- aoview_parse_string(data.state, sizeof (data.state), words[9]);
- aoview_parse_int(&data.tick, words[10]);
- aoview_parse_int(&data.accel, words[12]);
- aoview_parse_int(&data.pres, words[14]);
- aoview_parse_int(&data.temp, words[16]);
- aoview_parse_int(&data.batt, words[18]);
- aoview_parse_int(&data.drogue, words[20]);
- aoview_parse_int(&data.main, words[22]);
- aoview_parse_int(&data.flight_accel, words[24]);
- aoview_parse_int(&data.ground_accel, words[26]);
- aoview_parse_int(&data.flight_vel, words[28]);
- aoview_parse_int(&data.flight_pres, words[30]);
- aoview_parse_int(&data.ground_pres, words[32]);
- aoview_parse_int(&data.gps.nsat, words[34]);
- if (strcmp (words[36], "unlocked") == 0) {
- data.gps.gps_connected = 1;
- data.gps.gps_locked = 0;
- data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
- data.gps.lat = data.gps.lon = 0;
- data.gps.alt = 0;
- tracking_pos = 37;
- } else if (nword >= 40) {
- data.gps.gps_locked = 1;
- data.gps.gps_connected = 1;
- sscanf(words[36], "%d:%d:%d", &data.gps.gps_time.hour, &data.gps.gps_time.minute, &data.gps.gps_time.second);
- aoview_parse_pos(&data.gps.lat, words[37]);
- aoview_parse_pos(&data.gps.lon, words[38]);
- sscanf(words[39], "%dm", &data.gps.alt);
- tracking_pos = 46;
- } else {
- data.gps.gps_connected = 0;
- data.gps.gps_locked = 0;
- data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
- data.gps.lat = data.gps.lon = 0;
- data.gps.alt = 0;
- tracking_pos = -1;
- }
- if (nword >= 46) {
- data.gps.gps_extended = 1;
- sscanf(words[40], "%lfm/s", &data.gps.ground_speed);
- sscanf(words[41], "%d", &data.gps.course);
- sscanf(words[42], "%lfm/s", &data.gps.climb_rate);
- sscanf(words[43], "%lf", &data.gps.hdop);
- sscanf(words[44], "%d", &data.gps.h_error);
- sscanf(words[45], "%d", &data.gps.v_error);
- } else {
- data.gps.gps_extended = 0;
- data.gps.ground_speed = 0;
- data.gps.course = 0;
- data.gps.climb_rate = 0;
- data.gps.hdop = 0;
- data.gps.h_error = 0;
- data.gps.v_error = 0;
- }
- if (tracking_pos >= 0 && nword >= tracking_pos + 2 && strcmp(words[tracking_pos], "SAT") == 0) {
- int c, n, pos;
- aoview_parse_int(&n, words[tracking_pos + 1]);
- pos = tracking_pos + 2;
- if (nword >= pos + n * 3) {
- data.gps_tracking.channels = n;
- for (c = 0; c < n; c++) {
- aoview_parse_int(&data.gps_tracking.sats[c].svid,
- words[pos + 0]);
- aoview_parse_hex(&data.gps_tracking.sats[c].state,
- words[pos + 1]);
- aoview_parse_int(&data.gps_tracking.sats[c].c_n0,
- words[pos + 2]);
- pos += 3;
- }
- } else {
- data.gps_tracking.channels = 0;
- }
- } else {
- data.gps_tracking.channels = 0;
- }
- aoview_state_notify(&data);
- return TRUE;
-}
-
-static void
-aoview_monitor_callback(gpointer user_data,
- struct aoview_serial *serial,
- gint revents)
-{
- int c;
-
- if (revents & (G_IO_HUP|G_IO_ERR)) {
- aoview_monitor_disconnect();
- return;
- }
- if (revents & G_IO_IN) {
- for (;;) {
- c = aoview_serial_getc(serial);
- if (c == -1)
- break;
- if (c == '\r')
- continue;
- if (c == '\n') {
- monitor_line[monitor_pos] = '\0';
- if (monitor_pos) {
- if (aoview_monitor_parse(monitor_line)) {
- aoview_log_set_serial(aostate.data.serial);
- if (aoview_log_get_serial())
- aoview_log_printf ("%s\n", monitor_line);
- }
- }
- monitor_pos = 0;
- } else if (monitor_pos < MONITOR_LEN)
- monitor_line[monitor_pos++] = c;
- }
- }
-}
-
-gboolean
-aoview_monitor_connect(char *tty)
-{
- aoview_monitor_disconnect();
- monitor_serial = aoview_serial_open(tty);
- if (!monitor_serial)
- return FALSE;
- aoview_table_clear();
- aoview_state_reset();
- aoview_serial_set_callback(monitor_serial,
- aoview_monitor_callback);
- return TRUE;
-}
+++ /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 GtkFileChooser *replay_dialog;
-static GtkWidget *replay_ok;
-static FILE *replay_file;
-static int replay_tick;
-
-static int
-find_tick(char *line, gboolean *is_pad)
-{
- char *state = strstr(line, "STATE");
- if (!state)
- return -1;
- state = strchr(state, ' ');
- if (!state)
- return -1;
- while (*state == ' ')
- state++;
- *is_pad = strncmp(state, "pad", 3) == 0;
- while (*state && !isdigit(*state))
- state++;
- return atoi(state);
-}
-
-static void
-aoview_replay_close(void)
-{
- if (replay_file) {
- fclose(replay_file);
- replay_file = NULL;
- }
-}
-
-static char replay_line[1024];
-
-static gboolean
-aoview_replay_read(gpointer data);
-
-static gboolean
-aoview_replay_execute(gpointer data)
-{
- aoview_monitor_parse(replay_line);
- g_idle_add(aoview_replay_read, NULL);
- return FALSE;
-}
-
-static gboolean
-aoview_replay_read(gpointer data)
-{
- int tick;
- gboolean is_pad;
-
- if (!replay_file)
- return FALSE;
- if (fgets(replay_line, sizeof (replay_line), replay_file)) {
- tick = find_tick(replay_line, &is_pad);
- if (tick >= 0 && replay_tick >= 0 && !is_pad) {
- while (tick < replay_tick)
- tick += 65536;
- g_timeout_add((tick - replay_tick) * 10,
- aoview_replay_execute,
- NULL);
- } else {
- aoview_replay_execute(NULL);
- }
- replay_tick = tick;
- } else {
- aoview_replay_close();
- }
- return FALSE;
-}
-
-static void
-aoview_replay_open(GtkWidget *widget, gpointer data)
-{
- char *replay_file_name;
- GtkWidget *dialog;
-
- aoview_replay_close();
- replay_file_name = gtk_file_chooser_get_filename(replay_dialog);
- replay_file = fopen(replay_file_name, "r");
- if (!replay_file) {
- dialog = gtk_message_dialog_new(GTK_WINDOW(replay_dialog),
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- "Error loading file '%s': %s",
- replay_file_name, g_strerror(errno));
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
- } else {
- replay_tick = -1;
- aoview_state_reset();
- aoview_replay_read(NULL);
- }
- gtk_widget_hide(GTK_WIDGET(replay_dialog));
-}
-
-void
-aoview_replay_init(GladeXML *xml)
-{
- GtkFileFilter *telem_filter;
- GtkFileFilter *all_filter;
- GtkFileFilter *log_filter;
-
- telem_filter = gtk_file_filter_new();
- gtk_file_filter_add_pattern(telem_filter, "*.telem");
- gtk_file_filter_set_name(telem_filter, "Telemetry Files");
-
- log_filter = gtk_file_filter_new();
- gtk_file_filter_add_pattern(log_filter, "*.log");
- gtk_file_filter_set_name(log_filter, "Log Files");
-
- all_filter = gtk_file_filter_new();
- gtk_file_filter_add_pattern(all_filter, "*");
- gtk_file_filter_set_name(all_filter, "All Files");
-
- replay_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "ao_replay_dialog"));
- assert(replay_dialog);
- gtk_file_chooser_set_current_folder(replay_dialog, aoview_file_dir);
- gtk_file_chooser_add_filter(replay_dialog, telem_filter);
- gtk_file_chooser_add_filter(replay_dialog, log_filter);
- gtk_file_chooser_add_filter(replay_dialog, all_filter);
-
- replay_ok = glade_xml_get_widget(xml, "ao_replay_ok");
- assert(replay_ok);
- g_signal_connect(G_OBJECT(replay_ok), "clicked",
- G_CALLBACK(aoview_replay_open),
- replay_dialog);
-}
+++ /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"
-#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;
- aoview_serial_callback func = (aoview_serial_callback) callback;
- 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 (func)
- (*func)(user_data, serial, revents);
- 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) {
- g_source_destroy(&serial->source);
- 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);
- aoview_serial_set_callback(serial, NULL);
- return serial;
-}
-
-void
-aoview_serial_close(struct aoview_serial *serial)
-{
- g_source_remove_poll(&serial->source, &serial->poll_fd);
- close(serial->fd);
- g_source_destroy(&serial->source);
-}
-
-void
-aoview_serial_set_callback(struct aoview_serial *serial,
- aoview_serial_callback func)
-{
- g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL);
-}
+++ /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"
-#include <math.h>
-
-static inline double sqr(double a) { return a * a; };
-
-static void
-aoview_great_circle (double start_lat, double start_lon,
- double end_lat, double end_lon,
- double *dist, double *bearing)
-{
- const double rad = M_PI / 180;
- const double earth_radius = 6371.2 * 1000; /* in meters */
- double lat1 = rad * start_lat;
- double lon1 = rad * -start_lon;
- double lat2 = rad * end_lat;
- double lon2 = rad * -end_lon;
-
- double d_lat = lat2 - lat1;
- double d_lon = lon2 - lon1;
-
- /* From http://en.wikipedia.org/wiki/Great-circle_distance */
- double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
- sqr(cos(lat1) * sin(lat2) -
- sin(lat1) * cos(lat2) * cos(d_lon)));
- double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
- double d = atan2(vdn,vdd);
- double course;
-
- if (cos(lat1) < 1e-20) {
- if (lat1 > 0)
- course = M_PI;
- else
- course = -M_PI;
- } else {
- if (d < 1e-10)
- course = 0;
- else
- course = acos((sin(lat2)-sin(lat1)*cos(d)) /
- (sin(d)*cos(lat1)));
- if (sin(lon2-lon1) > 0)
- course = 2 * M_PI-course;
- }
- *dist = d * earth_radius;
- *bearing = course * 180/M_PI;
-}
-
-static void
-aoview_state_add_deg(int column, char *label, double deg, char pos, char neg)
-{
- double int_part;
- double min;
- char sign = pos;
-
- if (deg < 0) {
- deg = -deg;
- sign = neg;
- }
- int_part = floor (deg);
- min = (deg - int_part) * 60.0;
- aoview_table_add_row(column, label, "%d°%lf'%c",
- (int) int_part, min, sign);
-
-}
-
-static char *ascent_states[] = {
- "boost",
- "fast",
- "coast",
- 0,
-};
-
-static double
-aoview_time(void)
-{
- struct timespec now;
-
- clock_gettime(CLOCK_MONOTONIC, &now);
- return (double) now.tv_sec + (double) now.tv_nsec / 1.0e9;
-}
-
-/*
- * Fill out the derived data fields
- */
-static void
-aoview_state_derive(struct aodata *data, struct aostate *state)
-{
- int i;
- double new_height;
- double height_change;
- double time_change;
- int tick_count;
-
- state->report_time = aoview_time();
-
- state->prev_data = state->data;
- state->prev_npad = state->npad;
- state->data = *data;
- tick_count = data->tick;
- if (tick_count < state->prev_data.tick)
- tick_count += 65536;
- time_change = (tick_count - state->prev_data.tick) / 100.0;
-
- state->ground_altitude = aoview_pres_to_altitude(data->ground_pres);
- new_height = aoview_pres_to_altitude(data->flight_pres) - state->ground_altitude;
- height_change = new_height - state->height;
- state->height = new_height;
- if (time_change)
- state->baro_speed = (state->baro_speed * 3 + (height_change / time_change)) / 4.0;
- state->acceleration = (data->ground_accel - data->flight_accel) / 27.0;
- state->speed = data->flight_vel / 2700.0;
- state->temperature = ((data->temp / 32767.0 * 3.3) - 0.5) / 0.01;
- state->drogue_sense = data->drogue / 32767.0 * 15.0;
- state->main_sense = data->main / 32767.0 * 15.0;
- state->battery = data->batt / 32767.0 * 5.0;
- if (!strcmp(data->state, "pad")) {
- if (data->gps.gps_locked && data->gps.nsat >= 4) {
- state->npad++;
- state->pad_lat_total += data->gps.lat;
- state->pad_lon_total += data->gps.lon;
- state->pad_alt_total += data->gps.alt;
- if (state->npad > 1) {
- state->pad_lat = (state->pad_lat * 31 + data->gps.lat) / 32.0;
- state->pad_lon = (state->pad_lon * 31 + data->gps.lon) / 32.0;
- state->pad_alt = (state->pad_alt * 31 + data->gps.alt) / 32.0;
- } else {
- state->pad_lat = data->gps.lat;
- state->pad_lon = data->gps.lon;
- state->pad_alt = data->gps.alt;
- }
- }
- }
- state->ascent = FALSE;
- for (i = 0; ascent_states[i]; i++)
- if (!strcmp(data->state, ascent_states[i]))
- state->ascent = TRUE;
-
- /* Only look at accelerometer data on the way up */
- if (state->ascent && state->acceleration > state->max_acceleration)
- state->max_acceleration = state->acceleration;
- if (state->ascent && state->speed > state->max_speed)
- state->max_speed = state->speed;
-
- if (state->height > state->max_height)
- state->max_height = state->height;
- state->gps.gps_locked = data->gps.gps_locked;
- state->gps.gps_connected = data->gps.gps_connected;
- if (data->gps.gps_locked) {
- state->gps = data->gps;
- state->gps_valid = 1;
- if (state->npad)
- aoview_great_circle(state->pad_lat, state->pad_lon, state->gps.lat, state->gps.lon,
- &state->distance, &state->bearing);
- }
- if (data->gps_tracking.channels)
- state->gps_tracking = data->gps_tracking;
- if (state->npad) {
- state->gps_height = state->gps.alt - state->pad_alt;
- } else {
- state->gps_height = 0;
- }
-}
-
-void
-aoview_speak_state(struct aostate *state)
-{
- if (strcmp(state->data.state, state->prev_data.state)) {
- aoview_voice_speak("%s\n", state->data.state);
- if (!strcmp(state->data.state, "drogue"))
- aoview_voice_speak("apogee %d meters\n",
- (int) state->max_height);
- if (!strcmp(state->prev_data.state, "boost"))
- aoview_voice_speak("max speed %d meters per second\n",
- (int) state->max_speed);
- }
- if (state->prev_npad < MIN_PAD_SAMPLES && state->npad >= MIN_PAD_SAMPLES)
- aoview_voice_speak("g p s ready\n");
-}
-
-void
-aoview_speak_height(struct aostate *state)
-{
- aoview_voice_speak("%d meters\n", state->height);
-}
-
-struct aostate aostate;
-
-static guint aostate_timeout;
-
-#define COMPASS_LIMIT(n) ((n * 22.5) + 22.5/2)
-
-static char *compass_points[] = {
- "north",
- "north north east",
- "north east",
- "east north east",
- "east",
- "east south east",
- "south east",
- "south south east",
- "south",
- "south south west",
- "south west",
- "west south west",
- "west",
- "west north west",
- "north west",
- "north north west",
-};
-
-static char *
-aoview_compass_point(double bearing)
-{
- int i;
- while (bearing < 0)
- bearing += 360.0;
- while (bearing >= 360.0)
- bearing -= 360.0;
-
- i = floor ((bearing - 22.5/2) / 22.5 + 0.5);
- if (i < 0) i = 0;
- if (i >= sizeof (compass_points) / sizeof (compass_points[0]))
- i = 0;
- return compass_points[i];
-}
-
-static gboolean
-aoview_state_timeout(gpointer data)
-{
- double now = aoview_time();
-
- if (strlen(aostate.data.state) > 0 && strcmp(aostate.data.state, "pad") != 0)
- aoview_speak_height(&aostate);
- if (now - aostate.report_time >= 20 || !strcmp(aostate.data.state, "landed")) {
- if (!aostate.ascent) {
- if (fabs(aostate.baro_speed) < 20 && aostate.height < 100)
- aoview_voice_speak("rocket landed safely\n");
- else
- aoview_voice_speak("rocket may have crashed\n");
- if (aostate.gps_valid) {
- aoview_voice_speak("rocket reported %s of pad distance %d meters\n",
- aoview_compass_point(aostate.bearing),
- (int) aostate.distance);
- }
- }
- aostate_timeout = 0;
- return FALSE;
- }
- return TRUE;
-}
-
-void
-aoview_state_reset(void)
-{
- memset(&aostate, '\0', sizeof (aostate));
-}
-
-void
-aoview_state_notify(struct aodata *data)
-{
- struct aostate *state = &aostate;
- aoview_state_derive(data, state);
- aoview_table_start();
-
- if (state->npad >= MIN_PAD_SAMPLES)
- aoview_table_add_row(0, "Ground state", "ready");
- else
- aoview_table_add_row(0, "Ground state", "waiting for gps (%d)",
- MIN_PAD_SAMPLES - state->npad);
- aoview_table_add_row(0, "Rocket state", "%s", state->data.state);
- aoview_table_add_row(0, "Callsign", "%s", state->data.callsign);
- aoview_table_add_row(0, "Rocket serial", "%d", state->data.serial);
-
- aoview_table_add_row(0, "RSSI", "%6ddBm", state->data.rssi);
- aoview_table_add_row(0, "Height", "%6dm", state->height);
- aoview_table_add_row(0, "Max height", "%6dm", state->max_height);
- aoview_table_add_row(0, "Acceleration", "%7.1fm/s²", state->acceleration);
- aoview_table_add_row(0, "Max acceleration", "%7.1fm/s²", state->max_acceleration);
- aoview_table_add_row(0, "Speed", "%7.1fm/s", state->ascent ? state->speed : state->baro_speed);
- aoview_table_add_row(0, "Max Speed", "%7.1fm/s", state->max_speed);
- aoview_table_add_row(0, "Temperature", "%6.2f°C", state->temperature);
- aoview_table_add_row(0, "Battery", "%5.2fV", state->battery);
- aoview_table_add_row(0, "Drogue", "%5.2fV", state->drogue_sense);
- aoview_table_add_row(0, "Main", "%5.2fV", state->main_sense);
- aoview_table_add_row(0, "Pad altitude", "%dm", state->ground_altitude);
- aoview_table_add_row(1, "Satellites", "%d", state->gps.nsat);
- if (state->gps.gps_locked) {
- aoview_table_add_row(1, "GPS", "locked");
- } else if (state->gps.gps_connected) {
- aoview_table_add_row(1, "GPS", "unlocked");
- } else {
- aoview_table_add_row(1, "GPS", "not available");
- }
- if (state->gps_valid) {
- aoview_state_add_deg(1, "Latitude", state->gps.lat, 'N', 'S');
- aoview_state_add_deg(1, "Longitude", state->gps.lon, 'E', 'W');
- aoview_table_add_row(1, "GPS altitude", "%d", state->gps.alt);
- aoview_table_add_row(1, "GPS height", "%d", state->gps_height);
- aoview_table_add_row(1, "GPS time", "%02d:%02d:%02d",
- state->gps.gps_time.hour,
- state->gps.gps_time.minute,
- state->gps.gps_time.second);
- }
- if (state->gps.gps_extended) {
- aoview_table_add_row(1, "GPS ground speed", "%7.1fm/s %d°",
- state->gps.ground_speed,
- state->gps.course);
- aoview_table_add_row(1, "GPS climb rate", "%7.1fm/s",
- state->gps.climb_rate);
- aoview_table_add_row(1, "GPS precision", "%4.1f(hdop) %3dm(h) %3dm(v)",
- state->gps.hdop, state->gps.h_error, state->gps.v_error);
- }
- if (state->npad) {
- aoview_table_add_row(1, "Distance from pad", "%5.0fm", state->distance);
- aoview_table_add_row(1, "Direction from pad", "%4.0f°", state->bearing);
- aoview_state_add_deg(1, "Pad latitude", state->pad_lat, 'N', 'S');
- aoview_state_add_deg(1, "Pad longitude", state->pad_lon, 'E', 'W');
- aoview_table_add_row(1, "Pad GPS alt", "%gm", state->pad_alt);
- }
- if (state->gps.gps_connected) {
- int nsat_vis = 0;
- int nsat_locked = 0;
- int c;
-
- for (c = 0; c < state->gps_tracking.channels; c++) {
- if ((state->gps_tracking.sats[c].state & 0xff) == 0xbf)
- nsat_locked++;
- }
- aoview_table_add_row(2, "Satellites Visible", "%d", state->gps_tracking.channels);
- aoview_table_add_row(2, "Satellites Locked", "%d", nsat_locked);
- for (c = 0; c < state->gps_tracking.channels; c++) {
- aoview_table_add_row(2, "Satellite id,state,C/N0",
- "%3d,%02x,%2d%s",
- state->gps_tracking.sats[c].svid,
- state->gps_tracking.sats[c].state,
- state->gps_tracking.sats[c].c_n0,
- (state->gps_tracking.sats[c].state & 0xff) == 0xbf ?
- " LOCKED" : "");
- }
- }
- aoview_table_finish();
- aoview_label_show(state);
- aoview_speak_state(state);
- if (!aostate_timeout && strcmp(state->data.state, "pad") != 0)
- aostate_timeout = g_timeout_add_seconds(10, aoview_state_timeout, NULL);
-}
-
-void
-aoview_state_new(void)
-{
-}
-
-void
-aoview_state_init(GladeXML *xml)
-{
- aoview_state_new();
-}
+++ /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 NCOL 3
-
-static GtkTreeView *dataview[NCOL];
-static GtkListStore *datalist[NCOL];
-
-void
-aoview_table_start(void)
-{
- int col;
- for (col = 0; col < NCOL; col++)
- datalist[col] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
-}
-
-void
-aoview_table_add_row(int col, 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[col], &iter);
- gtk_list_store_set(datalist[col], &iter,
- 0, label,
- 1, buf,
- -1);
-}
-
-void
-aoview_table_finish(void)
-{
- int col;
- for (col = 0; col < NCOL; col++) {
- gtk_tree_view_set_model(dataview[col], GTK_TREE_MODEL(datalist[col]));
- g_object_unref(G_OBJECT(datalist[col]));
- gtk_tree_view_columns_autosize(dataview[col]);
- }
-}
-
-void
-aoview_table_clear(void)
-{
- int col;
- for (col = 0; col < NCOL; col++)
- gtk_tree_view_set_model(dataview[col], NULL);
-}
-
-void
-aoview_table_init(GladeXML *xml)
-{
- int col;
-
- for (col = 0; col < NCOL; col++) {
- char name[32];
- sprintf(name, "dataview_%d", col);
- dataview[col] = GTK_TREE_VIEW(glade_xml_get_widget(xml, name));
- assert(dataview[col]);
-
- aoview_add_plain_text_column(dataview[col], "Field", 0, 20);
- aoview_add_plain_text_column(dataview[col], "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;
-}
+++ /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"
-
-#if HAVE_FLITE
-#include <stdarg.h>
-
-FILE *aoview_flite;
-
-void aoview_voice_open(void)
-{
- int err;
-
- if (!aoview_flite)
- aoview_flite = aoview_flite_start();
-}
-
-void aoview_voice_close(void)
-{
- if (aoview_flite) {
- aoview_flite_stop();
- aoview_flite = NULL;
- }
-}
-
-void aoview_voice_speak(char *format, ...)
-{
- va_list ap;
-
- if (aoview_flite) {
- va_start(ap, format);
- vfprintf(aoview_flite, format, ap);
- fflush(aoview_flite);
- va_end(ap);
- }
-}
-
-#else
-void aoview_voice_open(void)
-{
-}
-
-void aoview_voice_close(void)
-{
-}
-
-void aoview_voice_speak(char *format, ...)
-{
-}
-#endif
-
-
-static GtkCheckMenuItem *voice_enable;
-
-#define ALTOS_VOICE_PATH "/apps/aoview/voice"
-
-static void
-aoview_voice_enable(GtkWidget *widget, gpointer data)
-{
- gboolean enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
- GError *error;
- GConfClient *gconf_client;
-
- if (enabled) {
- aoview_voice_open();
- aoview_voice_speak("enable voice\n");
- } else {
- aoview_voice_speak("disable voice\n");
- aoview_voice_close();
- }
- gconf_client = gconf_client_get_default();
- gconf_client_set_bool(gconf_client,
- ALTOS_VOICE_PATH,
- enabled,
- &error);
-}
-
-void
-aoview_voice_init(GladeXML *xml)
-{
- gboolean enabled;
- GConfClient *gconf_client;
-
- voice_enable = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "voice_enable"));
- assert(voice_enable);
-
- gconf_client = gconf_client_get_default();
- enabled = TRUE;
- if (gconf_client)
- {
- GError *error;
-
- error = NULL;
- enabled = gconf_client_get_bool(gconf_client,
- ALTOS_VOICE_PATH,
- &error);
- if (error)
- enabled = TRUE;
- }
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(voice_enable), enabled);
- if (enabled)
- aoview_voice_open();
-
- g_signal_connect(G_OBJECT(voice_enable), "toggled",
- G_CALLBACK(aoview_voice_enable),
- voice_enable);
-}
+++ /dev/null
-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
AC_OUTPUT([
Makefile
-ao-view/Makefile
ao-tools/Makefile
ao-tools/lib/Makefile
ao-tools/ao-rawload/Makefile
ao-tools/ao-bitbang/Makefile
ao-tools/ao-eeprom/Makefile
ao-tools/ao-load/Makefile
+ao-tools/ao-view/Makefile
ao-utils/Makefile
])