Initial Commit - Copy from Altus Metrum AltOS

This commit is contained in:
2024-06-25 19:03:04 +02:00
commit 13fc49c923
2048 changed files with 1206748 additions and 0 deletions

4
ao-tools/ao-view/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.o
aoview
aoview_glade.h
aoview_flite

View File

@@ -0,0 +1,38 @@
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=$(WARN_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_channel.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/$$/"/' $< > $@

View File

@@ -0,0 +1,50 @@
.\"
.\" 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

View File

@@ -0,0 +1,845 @@
<?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="channel_menu_item">
<property name="visible">True</property>
<property name="label" translatable="yes">_Channel</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu7">
<property name="visible">True</property>
<child>
<widget class="GtkRadioMenuItem" id="channel_0">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 0 (434.550MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_1">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 1 (434.650MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_2">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 2 (434.750MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_3">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 3 (434.850MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_4">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 4 (434.950MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_5">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 5 (435.050MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_6">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 6 (435.150MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_7">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 7 (435.250MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_8">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 8 (435.350MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="channel_9">
<property name="visible">True</property>
<property name="label" translatable="yes">Channel 9 (435.450MHz)</property>
<property name="use_underline">True</property>
<property name="draw_as_radio">True</property>
<property name="group">channel_0</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 &#xA9; 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; either version 2 of the License, or (at your option) any later version.
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 &lt;keithp@keithp.com&gt;</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">&lt;filename&gt;</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>

270
ao-tools/ao-view/aoview.h Normal file
View File

@@ -0,0 +1,270 @@
/*
* Copyright © 2009 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; 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.
*/
#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 aostate {
struct cc_telem data;
/* derived data */
struct cc_telem 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 cc_gps gps;
struct cc_gps_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_set_channel(int channel);
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 cc_telem *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;
/* aoview_channel.c */
int
aoview_channel_current(void);
void
aoview_channel_init(GladeXML *xml);
#endif /* _AOVIEW_H_ */

View File

@@ -0,0 +1,91 @@
/*
* 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 "aoview.h"
#define NUM_CHANNEL 10
static GtkRadioMenuItem *channel_item[NUM_CHANNEL];
int
aoview_channel_current(void)
{
int c;
for (c = 0; c < NUM_CHANNEL; c++)
if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(channel_item[c])))
return c;
return -1;
}
static void
aoview_channel_notify(int channel)
{
if (0 <= channel && channel < NUM_CHANNEL)
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(channel_item[channel]), TRUE);
}
#define ALTOS_CHANNEL_PATH "/apps/aoview/channel"
static void
aoview_channel_change(GtkWidget *widget, gpointer data)
{
gboolean enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
int c = (int) data;
GConfClient *gconf_client;
GError *error;
if (enabled) {
aoview_monitor_set_channel(c);
gconf_client = gconf_client_get_default();
gconf_client_set_int(gconf_client, ALTOS_CHANNEL_PATH, c, &error);
}
}
void
aoview_channel_init(GladeXML *xml)
{
int c;
GConfClient *gconf_client;
for (c = 0; c < NUM_CHANNEL; c++) {
char name[32];
sprintf(name, "channel_%d", c);
channel_item[c] = GTK_RADIO_MENU_ITEM(glade_xml_get_widget(xml, name));
assert(channel_item[c]);
g_signal_connect(G_OBJECT(channel_item[c]), "toggled",
G_CALLBACK(aoview_channel_change),
(gpointer) c);
}
gconf_client = gconf_client_get_default();
c = 0;
if (gconf_client)
{
GError *error;
error = NULL;
c = gconf_client_get_int(gconf_client,
ALTOS_CHANNEL_PATH,
&error);
if (error)
c = 0;
}
aoview_channel_notify(c);
}

View File

@@ -0,0 +1,67 @@
/*
* 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 "aoview.h"
static int16_t altitude_table[] = {
#include "altitude.h"
};
#define ALT_FRAC_SCALE (1 << ALT_FRAC_BITS)
#define ALT_FRAC_MASK (ALT_FRAC_SCALE - 1)
int16_t
aoview_pres_to_altitude(int16_t pres)
{
uint8_t o;
int16_t part;
if (pres < 0)
pres = 0;
o = pres >> ALT_FRAC_BITS;
part = pres & ALT_FRAC_MASK;
return ((int32_t) altitude_table[o] * (ALT_FRAC_SCALE - part) +
(int32_t) altitude_table[o+1] * part + (ALT_FRAC_SCALE >> 1)) >> ALT_FRAC_BITS;
}
int16_t
aoview_altitude_to_pres(int16_t alt)
{
int16_t span, sub_span;
uint8_t l, h, m;
int32_t pres;
l = 0;
h = NALT - 1;
while ((h - l) != 1) {
m = (l + h) >> 1;
if (altitude_table[m] < alt)
h = m;
else
l = m;
}
span = altitude_table[l] - altitude_table[h];
sub_span = altitude_table[l] - alt;
pres = ((((int32_t) l * (span - sub_span) + (int32_t) h * sub_span) << ALT_FRAC_BITS) + (span >> 1)) / span;
if (pres > 32767)
pres = 32767;
if (pres < 0)
pres = 0;
return (int16_t) pres;
}

View File

@@ -0,0 +1,174 @@
/*
* 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 "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_INT,
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);
}

View File

@@ -0,0 +1,160 @@
/*
* 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 "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) {
if (cmd == 'F')
aoview_file_set_flight(eeprom_file, b);
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);
}

View File

@@ -0,0 +1,236 @@
/*
* 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 "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 flight;
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];
char seq[20];
struct tm tm;
time_t now;
char *full;
int r;
if (file->file)
return TRUE;
if (file->failed)
return FALSE;
full = cc_make_filename(file->serial, file->flight, file->ext);
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;
}
}
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_set_flight(struct aoview_file *file, int flight)
{
if (flight != file->flight)
aoview_file_finish(file);
file->flight = flight;
}
int
aoview_file_get_flight(struct aoview_file *file)
{
return file->flight;
}
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);
}

View File

@@ -0,0 +1,142 @@
/*
* 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 <stdio.h>
#include <flite/flite.h>
#include "aoview.h"
#include <alsa/asoundlib.h>
cst_voice *register_cmu_us_kal16();
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) {
fprintf(stderr, "alsa open failed %s\n",
strerror(-err));
alsa_handle = NULL;
}
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();
#if HAVE_REGISTER_CMU_US_KAL16
voice = register_cmu_us_kal16();
#else
#if HAVE_REGISTER_CMU_US_KAL
voice = register_cmu_us_kal();
#endif
#endif
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;
}

View File

@@ -0,0 +1,74 @@
/*
* 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 "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);
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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 "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_set_flight(int flight)
{
aoview_file_set_flight(aoview_log, flight);
}
int
aoview_log_get_flight(void)
{
return aoview_file_get_flight(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);
}

View File

@@ -0,0 +1,114 @@
/*
* 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 "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();
}
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'},
{ 0, 0, 0, 0 }
};
for (;;) {
int c, temp;
c = getopt_long_only(argc, argv, "T:", long_options, &temp);
if (c == -1)
break;
switch (c) {
case 'T':
aoview_tty = optarg;
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_channel_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;
}

View File

@@ -0,0 +1,109 @@
/*
* 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 "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();
}
gboolean
aoview_monitor_parse(const char *input_line)
{
struct cc_telem telem;
if (!cc_telem_parse(input_line, &telem))
return FALSE;
aoview_state_notify(&telem);
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);
aoview_log_set_flight(aostate.data.flight);
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;
}
}
}
void
aoview_monitor_set_channel(int channel)
{
if (monitor_serial) {
aoview_serial_printf(monitor_serial, "m 0\n");
aoview_serial_printf(monitor_serial, "c r %d\n", channel);
aoview_serial_printf(monitor_serial, "m 1\n");
}
}
gboolean
aoview_monitor_connect(char *tty)
{
int channel;
aoview_monitor_disconnect();
monitor_serial = aoview_serial_open(tty);
if (!monitor_serial)
return FALSE;
aoview_table_clear();
aoview_state_reset();
channel = aoview_channel_current();
aoview_monitor_set_channel(channel);
aoview_serial_set_callback(monitor_serial,
aoview_monitor_callback);
return TRUE;
}

View File

@@ -0,0 +1,148 @@
/*
* 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 "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);
}

View File

@@ -0,0 +1,271 @@
/*
* 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 "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);
}

View File

@@ -0,0 +1,372 @@
/*
* 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 "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 cc_telem *data, struct aostate *state)
{
int i;
double new_height;
double height_change;
double time_change;
double accel_counts_per_mss;
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;
accel_counts_per_mss = ((data->accel_minus_g - data->accel_plus_g) / 2.0) / 9.80665;
state->acceleration = (data->ground_accel - data->flight_accel) / accel_counts_per_mss;
state->speed = data->flight_vel / (accel_counts_per_mss * 100.0);
state->temperature = cc_thermometer_to_temperature(data->temp);
state->drogue_sense = cc_ignitor_to_voltage(data->drogue);
state->main_sense = cc_ignitor_to_voltage(data->main);
state->battery = cc_battery_to_voltage(data->batt);
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 cc_telem *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, "Rocket flight", "%d", state->data.flight);
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 date", "%04d-%02d-%02d",
state->gps.gps_time.year,
state->gps.gps_time.month,
state->gps.gps_time.day);
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 c;
aoview_table_add_row(2, "Satellites Visible", "%d", state->gps_tracking.channels);
for (c = 0; c < state->gps_tracking.channels; c++) {
aoview_table_add_row(2, "Satellite id,C/N0",
"%3d,%2d",
state->gps_tracking.sats[c].svid,
state->gps_tracking.sats[c].c_n0);
}
}
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();
}

View File

@@ -0,0 +1,84 @@
/*
* 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 "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);
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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 "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;
}

View File

@@ -0,0 +1,123 @@
/*
* 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 "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);
}

27
ao-tools/ao-view/design Normal file
View File

@@ -0,0 +1,27 @@
Requirements:
real-time display of telemetry
off-line display of logged data
Logging of telemetry
Capture of logged data to disk
Input data:
accelerometer
barometer
thermometer
gps
drogue and main continuity
battery voltage
time
reported flight state
reported events
Computed data:
velocity (from accelerometer)
altitude
range
direction
Displays:
numeric display of current rocket status
(graphics come later)
text message log