diff options
author | Keith Packard <keithp@keithp.com> | 2010-11-24 21:00:52 -0800 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2010-11-24 21:00:52 -0800 |
commit | 51c7741040d95c5deece939dae5e4136cc04afc4 (patch) | |
tree | 96631ee5aa9fd0c8c45f019ff154cc4fc8ca85d0 | |
parent | d1dbe3b69e6f95ef8ecd4cf959863b922ab47c66 (diff) | |
parent | 4e47c44d335276cf0dc5ed3a0756e50c98c1b9b9 (diff) |
Merge branch 'buttonbox'
Conflicts:
doc/telemetrum-doc.xsl
Pull the buttbox version of the docs in as it had been updated.
Signed-off-by: Keith Packard <keithp@keithp.com>
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | altosui/.gitignore (renamed from ao-tools/altosui/.gitignore) | 0 | ||||
-rw-r--r-- | altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml (renamed from ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml) | 0 | ||||
-rw-r--r-- | altosui/AltOS Package Configuration.pmdoc/01altosui.xml (renamed from ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml) | 0 | ||||
-rw-r--r-- | altosui/AltOS Package Configuration.pmdoc/index.xml (renamed from ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml) | 0 | ||||
-rw-r--r-- | altosui/Altos.java (renamed from ao-tools/altosui/Altos.java) | 8 | ||||
-rw-r--r-- | altosui/AltosAscent.java | 335 | ||||
-rw-r--r-- | altosui/AltosCRCException.java (renamed from ao-tools/altosui/AltosCRCException.java) | 0 | ||||
-rw-r--r-- | altosui/AltosCSV.java (renamed from ao-tools/altosui/AltosCSV.java) | 0 | ||||
-rw-r--r-- | altosui/AltosCSVUI.java (renamed from ao-tools/altosui/AltosCSVUI.java) | 76 | ||||
-rw-r--r-- | altosui/AltosChannelMenu.java (renamed from ao-tools/altosui/AltosChannelMenu.java) | 36 | ||||
-rw-r--r-- | altosui/AltosConfig.java (renamed from ao-tools/altosui/AltosConfig.java) | 129 | ||||
-rw-r--r-- | altosui/AltosConfigUI.java (renamed from ao-tools/altosui/AltosConfigUI.java) | 142 | ||||
-rw-r--r-- | altosui/AltosConfigureUI.java | 187 | ||||
-rw-r--r-- | altosui/AltosConvert.java (renamed from ao-tools/altosui/AltosConvert.java) | 0 | ||||
-rw-r--r-- | altosui/AltosDataChooser.java (renamed from ao-tools/altosui/AltosLogfileChooser.java) | 21 | ||||
-rw-r--r-- | altosui/AltosDataPoint.java (renamed from ao-tools/altosui/AltosDataPoint.java) | 0 | ||||
-rw-r--r-- | altosui/AltosDataPointReader.java (renamed from ao-tools/altosui/AltosDataPointReader.java) | 5 | ||||
-rw-r--r-- | altosui/AltosDebug.java (renamed from ao-tools/altosui/AltosDebug.java) | 13 | ||||
-rw-r--r-- | altosui/AltosDescent.java | 353 | ||||
-rw-r--r-- | altosui/AltosDevice.java (renamed from ao-tools/altosui/AltosDevice.java) | 9 | ||||
-rw-r--r-- | altosui/AltosDeviceDialog.java (renamed from ao-tools/altosui/AltosDeviceDialog.java) | 1 | ||||
-rw-r--r-- | altosui/AltosDisplayThread.java (renamed from ao-tools/altosui/AltosDisplayThread.java) | 74 | ||||
-rw-r--r-- | altosui/AltosEepromDownload.java (renamed from ao-tools/altosui/AltosEepromDownload.java) | 48 | ||||
-rw-r--r-- | altosui/AltosEepromIterable.java (renamed from ao-tools/altosui/AltosEepromIterable.java) | 14 | ||||
-rw-r--r-- | altosui/AltosEepromMonitor.java (renamed from ao-tools/altosui/AltosEepromMonitor.java) | 10 | ||||
-rw-r--r-- | altosui/AltosEepromRecord.java (renamed from ao-tools/altosui/AltosEepromRecord.java) | 11 | ||||
-rw-r--r-- | altosui/AltosFile.java (renamed from ao-tools/altosui/AltosFile.java) | 2 | ||||
-rw-r--r-- | altosui/AltosFlash.java (renamed from ao-tools/altosui/AltosFlash.java) | 19 | ||||
-rw-r--r-- | altosui/AltosFlashUI.java (renamed from ao-tools/altosui/AltosFlashUI.java) | 14 | ||||
-rw-r--r-- | altosui/AltosFlightDisplay.java | 24 | ||||
-rw-r--r-- | altosui/AltosFlightInfoTableModel.java (renamed from ao-tools/altosui/AltosFlightInfoTableModel.java) | 77 | ||||
-rw-r--r-- | altosui/AltosFlightReader.java | 38 | ||||
-rw-r--r-- | altosui/AltosFlightStatus.java | 154 | ||||
-rw-r--r-- | altosui/AltosFlightStatusTableModel.java (renamed from ao-tools/altosui/AltosFlightStatusTableModel.java) | 0 | ||||
-rw-r--r-- | altosui/AltosFlightUI.java | 205 | ||||
-rw-r--r-- | altosui/AltosGPS.java (renamed from ao-tools/altosui/AltosGPS.java) | 2 | ||||
-rw-r--r-- | altosui/AltosGraph.java (renamed from ao-tools/altosui/AltosGraph.java) | 2 | ||||
-rw-r--r-- | altosui/AltosGraphTime.java (renamed from ao-tools/altosui/AltosGraphTime.java) | 3 | ||||
-rw-r--r-- | altosui/AltosGraphUI.java (renamed from ao-tools/altosui/AltosGraphUI.java) | 20 | ||||
-rw-r--r-- | altosui/AltosGreatCircle.java (renamed from ao-tools/altosui/AltosGreatCircle.java) | 27 | ||||
-rw-r--r-- | altosui/AltosHexfile.java (renamed from ao-tools/altosui/AltosHexfile.java) | 0 | ||||
-rw-r--r-- | altosui/AltosIgnite.java | 173 | ||||
-rw-r--r-- | altosui/AltosIgniteUI.java | 317 | ||||
-rw-r--r-- | altosui/AltosInfoTable.java (renamed from ao-tools/altosui/AltosInfoTable.java) | 73 | ||||
-rw-r--r-- | altosui/AltosKML.java (renamed from ao-tools/altosui/AltosKML.java) | 0 | ||||
-rw-r--r-- | altosui/AltosLanded.java | 212 | ||||
-rw-r--r-- | altosui/AltosLed.java | 54 | ||||
-rw-r--r-- | altosui/AltosLights.java | 73 | ||||
-rw-r--r-- | altosui/AltosLine.java (renamed from ao-tools/altosui/AltosLine.java) | 0 | ||||
-rw-r--r-- | altosui/AltosLog.java (renamed from ao-tools/altosui/AltosLog.java) | 29 | ||||
-rw-r--r-- | altosui/AltosPad.java | 269 | ||||
-rw-r--r-- | altosui/AltosParse.java (renamed from ao-tools/altosui/AltosParse.java) | 2 | ||||
-rw-r--r-- | altosui/AltosPreferences.java (renamed from ao-tools/altosui/AltosPreferences.java) | 20 | ||||
-rw-r--r-- | altosui/AltosReader.java (renamed from ao-tools/altosui/AltosReader.java) | 2 | ||||
-rw-r--r-- | altosui/AltosRecord.java (renamed from ao-tools/altosui/AltosRecord.java) | 2 | ||||
-rw-r--r-- | altosui/AltosRecordIterable.java (renamed from ao-tools/altosui/AltosRecordIterable.java) | 8 | ||||
-rw-r--r-- | altosui/AltosReplayReader.java (renamed from ao-tools/altosui/AltosReplayThread.java) | 30 | ||||
-rw-r--r-- | altosui/AltosRomconfig.java (renamed from ao-tools/altosui/AltosRomconfig.java) | 1 | ||||
-rw-r--r-- | altosui/AltosRomconfigUI.java (renamed from ao-tools/altosui/AltosRomconfigUI.java) | 2 | ||||
-rw-r--r-- | altosui/AltosSerial.java (renamed from ao-tools/altosui/AltosSerial.java) | 83 | ||||
-rw-r--r-- | altosui/AltosSerialInUseException.java | 28 | ||||
-rw-r--r-- | altosui/AltosSerialMonitor.java (renamed from ao-tools/altosui/AltosSerialMonitor.java) | 0 | ||||
-rw-r--r-- | altosui/AltosSiteMap.java | 391 | ||||
-rw-r--r-- | altosui/AltosSiteMapCache.java | 103 | ||||
-rw-r--r-- | altosui/AltosSiteMapTile.java | 112 | ||||
-rw-r--r-- | altosui/AltosState.java (renamed from ao-tools/altosui/AltosState.java) | 4 | ||||
-rw-r--r-- | altosui/AltosTelemetry.java (renamed from ao-tools/altosui/AltosTelemetry.java) | 4 | ||||
-rw-r--r-- | altosui/AltosTelemetryIterable.java (renamed from ao-tools/altosui/AltosTelemetryIterable.java) | 1 | ||||
-rw-r--r-- | altosui/AltosTelemetryReader.java | 61 | ||||
-rw-r--r-- | altosui/AltosUI.app/Contents/Info.plist (renamed from ao-tools/altosui/AltosUI.app/Contents/Info.plist) | 0 | ||||
-rwxr-xr-x | altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub (renamed from ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub) | bin | 61296 -> 61296 bytes | |||
-rw-r--r-- | altosui/AltosUI.app/Contents/PkgInfo (renamed from ao-tools/altosui/AltosUI.app/Contents/PkgInfo) | 0 | ||||
-rw-r--r-- | altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns (renamed from ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns) | bin | 129010 -> 129010 bytes | |||
-rw-r--r-- | altosui/AltosUI.java | 405 | ||||
-rw-r--r-- | altosui/AltosVoice.java (renamed from ao-tools/altosui/AltosVoice.java) | 0 | ||||
-rw-r--r-- | altosui/AltosWriter.java (renamed from ao-tools/altosui/AltosWriter.java) | 0 | ||||
-rw-r--r-- | altosui/GrabNDrag.java | 54 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe) | bin | 51831 -> 51831 bytes | |||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf) | 0 | ||||
-rw-r--r-- | altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys (renamed from ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys) | bin | 30464 -> 30464 bytes | |||
-rw-r--r-- | altosui/Instdrv/NSIS/Plugins/InstDrv.dll (renamed from ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll) | bin | 6656 -> 6656 bytes | |||
-rw-r--r-- | altosui/Makefile-standalone (renamed from ao-tools/altosui/Makefile-standalone) | 0 | ||||
-rw-r--r-- | altosui/Makefile.am (renamed from ao-tools/altosui/Makefile.am) | 64 | ||||
-rw-r--r-- | altosui/altos-windows.nsi (renamed from ao-tools/altosui/altos-windows.nsi) | 0 | ||||
-rwxr-xr-x | altosui/altosui-fat (renamed from ao-tools/altosui/altosui-fat) | 0 | ||||
-rw-r--r-- | altosui/altosui.1 (renamed from ao-tools/altosui/altosui.1) | 0 | ||||
-rw-r--r-- | altosui/altusmetrum.jpg (renamed from ao-tools/altosui/altusmetrum.jpg) | bin | 72868 -> 72868 bytes | |||
-rw-r--r-- | altosui/libaltos/.gitignore (renamed from ao-tools/libaltos/.gitignore) | 0 | ||||
-rw-r--r-- | altosui/libaltos/Makefile-standalone (renamed from ao-tools/libaltos/Makefile-standalone) | 0 | ||||
-rw-r--r-- | altosui/libaltos/Makefile.am (renamed from ao-tools/libaltos/Makefile.am) | 0 | ||||
-rwxr-xr-x | altosui/libaltos/altos.dll (renamed from ao-tools/libaltos/altos.dll) | bin | 31765 -> 31765 bytes | |||
-rw-r--r-- | altosui/libaltos/cjnitest.c (renamed from ao-tools/libaltos/cjnitest.c) | 0 | ||||
-rw-r--r-- | altosui/libaltos/libaltos.c (renamed from ao-tools/libaltos/libaltos.c) | 0 | ||||
-rwxr-xr-x | altosui/libaltos/libaltos.dylib (renamed from ao-tools/libaltos/libaltos.dylib) | bin | 54176 -> 54176 bytes | |||
-rw-r--r-- | altosui/libaltos/libaltos.h (renamed from ao-tools/libaltos/libaltos.h) | 0 | ||||
-rw-r--r-- | altosui/libaltos/libaltos.i0 (renamed from ao-tools/libaltos/libaltos.i0) | 0 | ||||
-rwxr-xr-x | ao-bringup/turnon_teledongle | 2 | ||||
-rwxr-xr-x | ao-bringup/turnon_telemetrum | 2 | ||||
-rw-r--r-- | ao-tools/Makefile.am | 2 | ||||
-rw-r--r-- | ao-tools/altosui/AltosGraphDataChooser.java | 84 | ||||
-rw-r--r-- | ao-tools/altosui/AltosStatusTable.java | 73 | ||||
-rw-r--r-- | ao-tools/altosui/AltosUI.java | 515 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | contrib/arch-linux/PKGBUILD-git.freetts | 38 | ||||
-rw-r--r-- | contrib/arch-linux/PKGBUILD-git.jcommon | 28 | ||||
-rw-r--r-- | contrib/arch-linux/PKGBUILD-git.jfreechart | 27 | ||||
-rw-r--r-- | contrib/arch-linux/PKGBUILD-git.nsis.patched | 34 | ||||
-rw-r--r-- | contrib/arch-linux/PKGBUILD-git.sdcc_patched | 34 | ||||
-rw-r--r-- | contrib/arch-linux/new.patch | 35 | ||||
-rw-r--r-- | contrib/arch-linux/nsis-2.43-64bit-fixes.patch | 211 | ||||
-rw-r--r-- | doc/.gitignore | 3 | ||||
-rw-r--r-- | doc/Makefile | 37 | ||||
-rw-r--r-- | doc/altos.xsl | 1441 | ||||
-rw-r--r-- | doc/telemetrum-doc.xsl | 694 | ||||
-rw-r--r-- | icon/grayled.png | bin | 0 -> 1528 bytes | |||
-rw-r--r-- | icon/grayon.png | bin | 0 -> 1873 bytes | |||
-rw-r--r-- | icon/greenled.png | bin | 0 -> 2281 bytes | |||
-rw-r--r-- | icon/greenoff.png | bin | 0 -> 1811 bytes | |||
-rw-r--r-- | icon/redled.png | bin | 0 -> 2103 bytes | |||
-rw-r--r-- | icon/redoff.png | bin | 0 -> 1694 bytes | |||
-rw-r--r-- | src/ao_ignite.c | 5 | ||||
-rw-r--r-- | src/ao_radio.c | 43 |
128 files changed, 6654 insertions, 1296 deletions
diff --git a/Makefile.am b/Makefile.am index 264e577a..2c412bf9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=src ao-tools ao-utils +SUBDIRS=src altosui ao-tools ao-utils EXTRA_DIST = ChangeLog diff --git a/ao-tools/altosui/.gitignore b/altosui/.gitignore index 89be1d53..89be1d53 100644 --- a/ao-tools/altosui/.gitignore +++ b/altosui/.gitignore diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml index 18e00fe4..18e00fe4 100644 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml +++ b/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui.xml index 6170931b..6170931b 100644 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/01altosui.xml +++ b/altosui/AltOS Package Configuration.pmdoc/01altosui.xml diff --git a/ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml b/altosui/AltOS Package Configuration.pmdoc/index.xml index fabe54a6..fabe54a6 100644 --- a/ao-tools/altosui/AltOS Package Configuration.pmdoc/index.xml +++ b/altosui/AltOS Package Configuration.pmdoc/index.xml diff --git a/ao-tools/altosui/Altos.java b/altosui/Altos.java index 997550e0..8ee94e04 100644 --- a/ao-tools/altosui/Altos.java +++ b/altosui/Altos.java @@ -67,6 +67,14 @@ public class Altos { static boolean map_initialized = false; + static final int tab_elt_pad = 5; + + static final Font label_font = new Font("Dialog", Font.PLAIN, 22); + static final Font value_font = new Font("Monospaced", Font.PLAIN, 22); + static final Font status_font = new Font("SansSerif", Font.BOLD, 24); + + static final int text_width = 16; + static void initialize_map() { string_to_state.put("startup", ao_flight_startup); diff --git a/altosui/AltosAscent.java b/altosui/AltosAscent.java new file mode 100644 index 00000000..64bdcf30 --- /dev/null +++ b/altosui/AltosAscent.java @@ -0,0 +1,335 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosAscent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class AscentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public AscentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public class AscentValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public AscentValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.gridwidth = 2; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + + public class AscentValueHold { + JLabel label; + JTextField value; + JTextField max_value; + double max; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + max_value.setText(""); + max = 0; + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + if (v > max) { + max_value.setText(String.format(format, v)); + max = v; + } + } + public AscentValueHold (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + max_value = new JTextField(Altos.text_width); + max_value.setFont(Altos.value_font); + max_value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 3; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(max_value, c); + add(max_value); + } + } + + + class Height extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Height"); + } + } + + Height height; + + class Speed extends AscentValueHold { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Speed"); + } + } + + Speed speed; + + class Accel extends AscentValueHold { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Acceleration"); + } + } + + Accel accel; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Apogee extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends AscentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class Lat extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends AscentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + public void reset() { + lat.reset(); + lon.reset(); + main.reset(); + apogee.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public void labels(GridBagLayout layout, int y) { + GridBagConstraints c; + JLabel cur, max; + + cur = new JLabel("Current"); + cur.setFont(Altos.label_font); + c = new GridBagConstraints(); + c.gridx = 2; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + layout.setConstraints(cur, c); + add(cur); + + max = new JLabel("Maximum"); + max.setFont(Altos.label_font); + c.gridx = 3; c.gridy = y; + layout.setConstraints(max, c); + add(max); + } + + public AltosAscent() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in ascent display: + * + * lat + * lon + * height + */ + labels(layout, 0); + height = new Height(layout, 1); + speed = new Speed(layout, 2); + accel = new Accel(layout, 3); + lat = new Lat(layout, 4); + lon = new Lon(layout, 5); + apogee = new Apogee(layout, 6); + main = new Main(layout, 7); + } +} diff --git a/ao-tools/altosui/AltosCRCException.java b/altosui/AltosCRCException.java index 4a529bcf..4a529bcf 100644 --- a/ao-tools/altosui/AltosCRCException.java +++ b/altosui/AltosCRCException.java diff --git a/ao-tools/altosui/AltosCSV.java b/altosui/AltosCSV.java index df98b2b4..df98b2b4 100644 --- a/ao-tools/altosui/AltosCSV.java +++ b/altosui/AltosCSV.java diff --git a/ao-tools/altosui/AltosCSVUI.java b/altosui/AltosCSVUI.java index eb620ba8..e1b6002d 100644 --- a/ao-tools/altosui/AltosCSVUI.java +++ b/altosui/AltosCSVUI.java @@ -28,21 +28,17 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosLogfileChooser; -import altosui.AltosCSV; - public class AltosCSVUI extends JDialog - implements Runnable, ActionListener + implements ActionListener { - JFrame frame; - Thread thread; - AltosRecordIterable iterable; - AltosWriter writer; JFileChooser csv_chooser; + JPanel accessory; JComboBox combo_box; + AltosRecordIterable iterable; + AltosWriter writer; - static String[] combo_box_items = { "CSV", "KML" }; + static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" }; void set_default_file() { File current = csv_chooser.getSelectedFile(); @@ -50,57 +46,63 @@ public class AltosCSVUI String new_name = null; String selected = (String) combo_box.getSelectedItem(); - if (selected.equals("CSV")) + if (selected.contains("CSV")) new_name = Altos.replace_extension(current_name, ".csv"); - else if (selected.equals("KML")) + else if (selected.contains("KML")) new_name = Altos.replace_extension(current_name, ".kml"); if (new_name != null) csv_chooser.setSelectedFile(new File(new_name)); } - public void run() { - AltosLogfileChooser chooser; + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("comboBoxChanged")) + set_default_file(); + } + + public AltosCSVUI(JFrame frame, AltosRecordIterable in_iterable, File source_file) { + iterable = in_iterable; + csv_chooser = new JFileChooser(source_file); - chooser = new AltosLogfileChooser(frame); - iterable = chooser.runDialog(); - if (iterable == null) - return; + accessory = new JPanel(); + accessory.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.weightx = 1; + c.weighty = 0; + c.insets = new Insets (4, 4, 4, 4); + + JLabel accessory_label = new JLabel("Export File Type"); + c.gridx = 0; + c.gridy = 0; + accessory.add(accessory_label, c); - csv_chooser = new JFileChooser(chooser.file()); combo_box = new JComboBox(combo_box_items); combo_box.addActionListener(this); - csv_chooser.setAccessory(combo_box); - csv_chooser.setSelectedFile(chooser.file()); + c.gridx = 0; + c.gridy = 1; + accessory.add(combo_box, c); + + csv_chooser.setAccessory(accessory); + csv_chooser.setSelectedFile(source_file); set_default_file(); int ret = csv_chooser.showSaveDialog(frame); if (ret == JFileChooser.APPROVE_OPTION) { - File file = csv_chooser.getSelectedFile(); - String type = (String) combo_box.getSelectedItem(); + File file = csv_chooser.getSelectedFile(); + String type = (String) combo_box.getSelectedItem(); try { - if (type.equals("CSV")) + if (type.contains("CSV")) writer = new AltosCSV(file); else writer = new AltosKML(file); + writer.write(iterable); + writer.close(); } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(frame, file.getName(), "Cannot open file", JOptionPane.ERROR_MESSAGE); } - writer.write(iterable); - writer.close(); } } - - public void actionPerformed(ActionEvent e) { - System.out.printf("command %s param %s\n", e.getActionCommand(), e.paramString()); - if (e.getActionCommand().equals("comboBoxChanged")) - set_default_file(); - } - - public AltosCSVUI(JFrame in_frame) { - frame = in_frame; - thread = new Thread(this); - thread.start(); - } } diff --git a/ao-tools/altosui/AltosChannelMenu.java b/altosui/AltosChannelMenu.java index 504c13c6..abbb86f4 100644 --- a/ao-tools/altosui/AltosChannelMenu.java +++ b/altosui/AltosChannelMenu.java @@ -28,43 +28,17 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -public class AltosChannelMenu extends JMenu implements ActionListener { - ButtonGroup group; +public class AltosChannelMenu extends JComboBox implements ActionListener { int channel; - LinkedList<ActionListener> listeners; - - public void addActionListener(ActionListener l) { - listeners.add(l); - } - - public void actionPerformed(ActionEvent e) { - channel = Integer.parseInt(e.getActionCommand()); - - ListIterator<ActionListener> i = listeners.listIterator(); - - ActionEvent newe = new ActionEvent(this, channel, e.getActionCommand()); - while (i.hasNext()) { - ActionListener listener = i.next(); - listener.actionPerformed(newe); - } - } public AltosChannelMenu(int current_channel) { - super("Channel", true); - group = new ButtonGroup(); channel = current_channel; - listeners = new LinkedList<ActionListener>(); - for (int c = 0; c <= 9; c++) { - JRadioButtonMenuItem radioitem = new JRadioButtonMenuItem(String.format("Channel %1d (%7.3fMHz)", c, - 434.550 + c * 0.1), - c == channel); - radioitem.setActionCommand(String.format("%d", c)); - radioitem.addActionListener(this); - add(radioitem); - group.add(radioitem); - } + for (int c = 0; c <= 9; c++) + addItem(String.format("Channel %1d (%7.3fMHz)", c, 434.550 + c * 0.1)); + setSelectedIndex(channel); + setMaximumRowCount(10); } } diff --git a/ao-tools/altosui/AltosConfig.java b/altosui/AltosConfig.java index 7b6cd78c..1c42870f 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/altosui/AltosConfig.java @@ -26,21 +26,7 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosConfigUI; +import java.util.concurrent.*; import libaltosJNI.*; @@ -83,11 +69,12 @@ public class AltosConfig implements Runnable, ActionListener { int_ref main_deploy; int_ref apogee_delay; int_ref radio_channel; + int_ref radio_calibration; string_ref version; string_ref product; string_ref callsign; AltosConfigUI config_ui; - + boolean serial_started; boolean get_int(String line, String label, int_ref x) { if (line.startsWith(label)) { @@ -121,31 +108,37 @@ public class AltosConfig implements Runnable, ActionListener { } void start_serial() throws InterruptedException { + serial_started = true; if (remote) { - serial_line.set_channel(AltosPreferences.channel()); - serial_line.set_callsign(AltosPreferences.callsign()); - serial_line.printf("p\n"); + serial_line.set_radio(); + serial_line.printf("p\nE 0\n"); serial_line.flush_input(); } } void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; if (remote) { serial_line.printf("~"); serial_line.flush_output(); } } - void get_data() throws InterruptedException { + void get_data() throws InterruptedException, TimeoutException { try { start_serial(); serial_line.printf("c s\nv\n"); for (;;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(5000); + if (line == null) + throw new TimeoutException(); get_int(line, "serial-number", serial); get_int(line, "Main deploy:", main_deploy); get_int(line, "Apogee delay:", apogee_delay); get_int(line, "Radio channel:", radio_channel); + get_int(line, "Radio cal:", radio_calibration); get_string(line, "Callsign:", callsign); get_string(line,"software-version", version); get_string(line,"product", product); @@ -159,26 +152,38 @@ public class AltosConfig implements Runnable, ActionListener { } } - void init_ui () { - config_ui = new AltosConfigUI(owner); + void init_ui () throws InterruptedException, TimeoutException { + config_ui = new AltosConfigUI(owner, remote); config_ui.addActionListener(this); set_ui(); } - void set_ui() { + void abort() { + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); try { - if (serial_line != null) - get_data(); - config_ui.set_serial(serial.get()); - config_ui.set_product(product.get()); - config_ui.set_version(version.get()); - config_ui.set_main_deploy(main_deploy.get()); - config_ui.set_apogee_delay(apogee_delay.get()); - config_ui.set_radio_channel(radio_channel.get()); - config_ui.set_callsign(callsign.get()); - config_ui.set_clean(); + stop_serial(); } catch (InterruptedException ie) { } + serial_line.close(); + serial_line = null; + } + + void set_ui() throws InterruptedException, TimeoutException { + if (serial_line != null) + get_data(); + config_ui.set_serial(serial.get()); + config_ui.set_product(product.get()); + config_ui.set_version(version.get()); + config_ui.set_main_deploy(main_deploy.get()); + config_ui.set_apogee_delay(apogee_delay.get()); + config_ui.set_radio_channel(radio_channel.get()); + config_ui.set_radio_calibration(radio_calibration.get()); + config_ui.set_callsign(callsign.get()); + config_ui.set_clean(); } void run_dialog() { @@ -188,12 +193,16 @@ public class AltosConfig implements Runnable, ActionListener { main_deploy.set(config_ui.main_deploy()); apogee_delay.set(config_ui.apogee_delay()); radio_channel.set(config_ui.radio_channel()); + radio_calibration.set(config_ui.radio_calibration()); callsign.set(config_ui.callsign()); try { start_serial(); serial_line.printf("c m %d\n", main_deploy.get()); serial_line.printf("c d %d\n", apogee_delay.get()); - serial_line.printf("c r %d\n", radio_channel.get()); + if (!remote) { + serial_line.printf("c r %d\n", radio_channel.get()); + serial_line.printf("c f %d\n", radio_calibration.get()); + } serial_line.printf("c c %s\n", callsign.get()); serial_line.printf("c w\n"); } catch (InterruptedException ie) { @@ -207,14 +216,28 @@ public class AltosConfig implements Runnable, ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("save")) { - save_data(); - set_ui(); - } else if (cmd.equals("reset")) { - set_ui(); - } else if (cmd.equals("close")) { - if (serial_line != null) - serial_line.close(); + try { + if (cmd.equals("Save")) { + save_data(); + set_ui(); + } else if (cmd.equals("Reset")) { + set_ui(); + } else if (cmd.equals("Reboot")) { + if (serial_line != null) { + start_serial(); + serial_line.printf("r eboot\n"); + serial_line.flush_output(); + stop_serial(); + serial_line.close(); + } + } else if (cmd.equals("Close")) { + if (serial_line != null) + serial_line.close(); + } + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); } } @@ -222,8 +245,10 @@ public class AltosConfig implements Runnable, ActionListener { try { init_ui(); config_ui.make_visible(); -// } catch (InterruptedException ie) { - } finally { + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); } } @@ -234,15 +259,15 @@ public class AltosConfig implements Runnable, ActionListener { main_deploy = new int_ref(250); apogee_delay = new int_ref(0); radio_channel = new int_ref(0); + radio_calibration = new int_ref(1186611); callsign = new string_ref("N0CALL"); version = new string_ref("unknown"); product = new string_ref("unknown"); device = AltosDeviceDialog.show(owner, AltosDevice.product_any); - serial_line = new AltosSerial(); if (device != null) { try { - serial_line.open(device); + serial_line = new AltosSerial(device); if (!device.matchProduct(AltosDevice.product_telemetrum)) remote = true; config_thread = new Thread(this); @@ -250,12 +275,18 @@ public class AltosConfig implements Runnable, ActionListener { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(owner, String.format("Cannot open device \"%s\"", - device.getPath()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(owner, - device.getPath(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java index 605ccc8b..cfa5d7b9 100644 --- a/ao-tools/altosui/AltosConfigUI.java +++ b/altosui/AltosConfigUI.java @@ -29,19 +29,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; - import libaltosJNI.*; public class AltosConfigUI @@ -57,6 +44,7 @@ public class AltosConfigUI JLabel main_deploy_label; JLabel apogee_delay_label; JLabel radio_channel_label; + JLabel radio_calibration_label; JLabel callsign_label; public boolean dirty; @@ -68,10 +56,12 @@ public class AltosConfigUI JComboBox main_deploy_value; JComboBox apogee_delay_value; JComboBox radio_channel_value; + JTextField radio_calibration_value; JTextField callsign_value; JButton save; JButton reset; + JButton reboot; JButton close; ActionListener listener; @@ -103,12 +93,12 @@ public class AltosConfigUI public void windowClosing(WindowEvent e) { ui.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, - "close")); + "Close")); } } /* Build the UI using a grid bag */ - public AltosConfigUI(JFrame in_owner) { + public AltosConfigUI(JFrame in_owner, boolean remote) { super (in_owner, "Configure TeleMetrum", false); owner = in_owner; @@ -123,7 +113,7 @@ public class AltosConfigUI /* Product */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -131,8 +121,8 @@ public class AltosConfigUI pane.add(product_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 0; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 0; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -143,7 +133,7 @@ public class AltosConfigUI /* Version */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 1; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -152,8 +142,8 @@ public class AltosConfigUI pane.add(version_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 1; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 1; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -165,7 +155,7 @@ public class AltosConfigUI /* Serial */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 2; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -174,8 +164,8 @@ public class AltosConfigUI pane.add(serial_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 2; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 2; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -187,7 +177,7 @@ public class AltosConfigUI /* Main deploy */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 3; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -196,8 +186,8 @@ public class AltosConfigUI pane.add(main_deploy_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 3; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -211,7 +201,7 @@ public class AltosConfigUI /* Apogee delay */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 4; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -220,8 +210,8 @@ public class AltosConfigUI pane.add(apogee_delay_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 4; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 4; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -235,7 +225,7 @@ public class AltosConfigUI /* Radio channel */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 5; - c.gridwidth = 3; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -244,8 +234,8 @@ public class AltosConfigUI pane.add(radio_channel_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 5; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 5; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; @@ -254,12 +244,39 @@ public class AltosConfigUI radio_channel_value = new JComboBox(radio_channel_values); radio_channel_value.setEditable(false); radio_channel_value.addItemListener(this); + if (remote) + radio_channel_value.setEnabled(false); pane.add(radio_channel_value, c); - /* Callsign */ + /* Radio Calibration */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = 6; - c.gridwidth = 3; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + radio_calibration_label = new JLabel("RF Calibration:"); + pane.add(radio_calibration_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = 6; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + radio_calibration_value = new JTextField(String.format("%d", 1186611)); + radio_calibration_value.getDocument().addDocumentListener(this); + if (remote) + radio_calibration_value.setEnabled(false); + pane.add(radio_calibration_value, c); + + /* Callsign */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = 7; + c.gridwidth = 4; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; @@ -268,50 +285,61 @@ public class AltosConfigUI pane.add(callsign_label, c); c = new GridBagConstraints(); - c.gridx = 3; c.gridy = 6; - c.gridwidth = 3; + c.gridx = 4; c.gridy = 7; + c.gridwidth = 4; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - callsign_value = new JTextField("N0CALL"); + callsign_value = new JTextField(AltosPreferences.callsign()); callsign_value.getDocument().addDocumentListener(this); pane.add(callsign_value, c); /* Buttons */ c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; - c.gridwidth = 6; + c.gridx = 0; c.gridy = 8; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = il; save = new JButton("Save"); pane.add(save, c); save.addActionListener(this); - save.setActionCommand("save"); + save.setActionCommand("Save"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; - c.gridwidth = 6; + c.gridx = 2; c.gridy = 8; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.CENTER; c.insets = il; reset = new JButton("Reset"); pane.add(reset, c); reset.addActionListener(this); - reset.setActionCommand("reset"); + reset.setActionCommand("Reset"); c = new GridBagConstraints(); - c.gridx = 0; c.gridy = 7; - c.gridwidth = 6; + c.gridx = 4; c.gridy = 8; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = il; + reboot = new JButton("Reboot"); + pane.add(reboot, c); + reboot.addActionListener(this); + reboot.setActionCommand("Reboot"); + + c = new GridBagConstraints(); + c.gridx = 6; c.gridy = 8; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_END; c.insets = il; close = new JButton("Close"); pane.add(close, c); close.addActionListener(this); - close.setActionCommand("close"); + close.setActionCommand("Close"); addWindowListener(new ConfigListener(this)); } @@ -324,12 +352,12 @@ public class AltosConfigUI } /* If any values have been changed, confirm before closing */ - public boolean check_dirty() { + public boolean check_dirty(String operation) { if (dirty) { - Object[] options = { "Close anyway", "Keep editing" }; + Object[] options = { String.format("%s anyway", operation), "Keep editing" }; int i; i = JOptionPane.showOptionDialog(this, - "Configuration modified, close anyway?", + String.format("Configuration modified. %s anyway?", operation), "Configuration Modified", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, @@ -344,11 +372,11 @@ public class AltosConfigUI public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("close")) - if (!check_dirty()) + if (cmd.equals("Close") || cmd.equals("Reboot")) + if (!check_dirty(cmd)) return; listener.actionPerformed(e); - if (cmd.equals("close")) { + if (cmd.equals("Close") || cmd.equals("Reboot")) { setVisible(false); dispose(); } @@ -415,6 +443,14 @@ public class AltosConfigUI return radio_channel_value.getSelectedIndex(); } + public void set_radio_calibration(int new_radio_calibration) { + radio_calibration_value.setText(String.format("%d", new_radio_calibration)); + } + + public int radio_calibration() { + return Integer.parseInt(radio_calibration_value.getText()); + } + public void set_callsign(String new_callsign) { callsign_value.setText(new_callsign); } diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java new file mode 100644 index 00000000..153c59fd --- /dev/null +++ b/altosui/AltosConfigureUI.java @@ -0,0 +1,187 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosConfigureUI + extends JDialog + implements DocumentListener +{ + JFrame owner; + AltosVoice voice; + Container pane; + + JRadioButton enable_voice; + JButton test_voice; + JButton close; + + JButton configure_log; + JTextField log_directory; + + JLabel callsign_label; + JTextField callsign_value; + + /* DocumentListener interface methods */ + public void changedUpdate(DocumentEvent e) { + AltosPreferences.set_callsign(callsign_value.getText()); + } + + public void insertUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public void removeUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public AltosConfigureUI(JFrame in_owner, AltosVoice in_voice) { + super(in_owner, "Configure AltosUI", false); + + GridBagConstraints c; + + Insets insets = new Insets(4, 4, 4, 4); + + owner = in_owner; + voice = in_voice; + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + c = new GridBagConstraints(); + c.insets = insets; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + + /* Nice label at the top */ + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + pane.add(new JLabel ("Configure AltOS UI"), c); + + /* Voice settings */ + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Voice"), c); + + enable_voice = new JRadioButton("Enable", AltosPreferences.voice()); + enable_voice.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JRadioButton item = (JRadioButton) e.getSource(); + boolean enabled = item.isSelected(); + AltosPreferences.set_voice(enabled); + if (enabled) + voice.speak_always("Enable voice."); + else + voice.speak_always("Disable voice."); + } + }); + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.weightx = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(enable_voice, c); + + c.gridx = 2; + c.gridy = 1; + c.gridwidth = 1; + c.weightx = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.EAST; + test_voice = new JButton("Test Voice"); + test_voice.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + voice.speak("That's one small step for man; one giant leap for mankind."); + } + }); + pane.add(test_voice, c); + + /* Log directory settings */ + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Log Directory"), c); + + configure_log = new JButton(AltosPreferences.logdir().getPath()); + configure_log.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AltosPreferences.ConfigureLog(); + configure_log.setText(AltosPreferences.logdir().getPath()); + } + }); + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 2; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + pane.add(configure_log, c); + + /* Callsign setting */ + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Callsign"), c); + + callsign_value = new JTextField(AltosPreferences.callsign()); + callsign_value.getDocument().addDocumentListener(this); + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 2; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + pane.add(callsign_value, c); + + /* And a close button at the bottom */ + close = new JButton("Close"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + }); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + pane.add(close, c); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } +} diff --git a/ao-tools/altosui/AltosConvert.java b/altosui/AltosConvert.java index 8cc1df27..8cc1df27 100644 --- a/ao-tools/altosui/AltosConvert.java +++ b/altosui/AltosConvert.java diff --git a/ao-tools/altosui/AltosLogfileChooser.java b/altosui/AltosDataChooser.java index 8b9d77d6..15de05c2 100644 --- a/ao-tools/altosui/AltosLogfileChooser.java +++ b/altosui/AltosDataChooser.java @@ -27,7 +27,7 @@ import java.util.*; import java.text.*; import java.util.prefs.*; -public class AltosLogfileChooser extends JFileChooser { +public class AltosDataChooser extends JFileChooser { JFrame frame; String filename; File file; @@ -50,13 +50,15 @@ public class AltosLogfileChooser extends JFileChooser { return null; filename = file.getName(); try { - FileInputStream in; - - in = new FileInputStream(file); - if (filename.endsWith("eeprom")) + if (filename.endsWith("eeprom")) { + FileInputStream in = new FileInputStream(file); return new AltosEepromIterable(in); - else + } else if (filename.endsWith("telem")) { + FileInputStream in = new FileInputStream(file); return new AltosTelemetryIterable(in); + } else { + throw new FileNotFoundException(); + } } catch (FileNotFoundException fe) { JOptionPane.showMessageDialog(frame, filename, @@ -67,12 +69,11 @@ public class AltosLogfileChooser extends JFileChooser { return null; } - public AltosLogfileChooser(JFrame in_frame) { + public AltosDataChooser(JFrame in_frame) { frame = in_frame; setDialogTitle("Select Flight Record File"); setFileFilter(new FileNameExtensionFilter("Flight data file", - "eeprom", - "telem")); + "telem", "eeprom")); setCurrentDirectory(AltosPreferences.logdir()); } -}
\ No newline at end of file +} diff --git a/ao-tools/altosui/AltosDataPoint.java b/altosui/AltosDataPoint.java index 66313e03..66313e03 100644 --- a/ao-tools/altosui/AltosDataPoint.java +++ b/altosui/AltosDataPoint.java diff --git a/ao-tools/altosui/AltosDataPointReader.java b/altosui/AltosDataPointReader.java index 4d7831e4..7704310b 100644 --- a/ao-tools/altosui/AltosDataPointReader.java +++ b/altosui/AltosDataPointReader.java @@ -10,11 +10,6 @@ import java.lang.UnsupportedOperationException; import java.util.NoSuchElementException; import java.util.Iterator; -import altosui.AltosDataPoint; -import altosui.AltosRecordIterable; -import altosui.AltosRecord; -import altosui.AltosState; - class AltosDataPointReader implements Iterable<AltosDataPoint> { Iterator<AltosRecord> iter; AltosState state; diff --git a/ao-tools/altosui/AltosDebug.java b/altosui/AltosDebug.java index 3f469d48..8d435b66 100644 --- a/ao-tools/altosui/AltosDebug.java +++ b/altosui/AltosDebug.java @@ -19,11 +19,10 @@ package altosui; import java.lang.*; import java.io.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.LinkedList; -import java.util.Iterator; -import altosui.AltosSerial; -import altosui.AltosRomconfig; +import java.util.concurrent.*; +import java.util.*; + +import libaltosJNI.*; public class AltosDebug extends AltosSerial { @@ -261,4 +260,8 @@ public class AltosDebug extends AltosSerial { public void reset() { printf ("R\n"); } + + public AltosDebug (AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + super(in_device); + } }
\ No newline at end of file diff --git a/altosui/AltosDescent.java b/altosui/AltosDescent.java new file mode 100644 index 00000000..16ccd458 --- /dev/null +++ b/altosui/AltosDescent.java @@ -0,0 +1,353 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosDescent extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public abstract class DescentStatus { + JLabel label; + JTextField value; + AltosLights lights; + + abstract void show(AltosState state, int crc_errors); + void reset() { + value.setText(""); + lights.set(false); + } + + public DescentStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.gridwidth = 3; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 4; c.gridy = y; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public abstract class DescentValue { + JLabel label; + JTextField value; + + void reset() { + value.setText(""); + } + + abstract void show(AltosState state, int crc_errors); + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + void show(String v) { + value.setText(v); + } + + public DescentValue (GridBagLayout layout, int x, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = x + 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + add(label, c); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 2; c.gridy = y; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + add(value, c); + } + } + + public abstract class DescentDualValue { + JLabel label; + JTextField value1; + JTextField value2; + + void reset() { + value1.setText(""); + value2.setText(""); + } + + abstract void show(AltosState state, int crc_errors); + void show(String v1, String v2) { + value1.setText(v1); + value2.setText(v2); + } + void show(String f1, double v1, String f2, double v2) { + value1.setText(String.format(f1, v1)); + value2.setText(String.format(f2, v2)); + } + + public DescentDualValue (GridBagLayout layout, int x, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = x + 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value1 = new JTextField(Altos.text_width); + value1.setFont(Altos.value_font); + value1.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value1, c); + add(value1); + + value2 = new JTextField(Altos.text_width); + value2.setFont(Altos.value_font); + value2.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = x + 4; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.gridwidth = 1; + layout.setConstraints(value2, c); + add(value2); + } + } + + class Height extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.height); + } + public Height (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Height"); + } + } + + Height height; + + class Speed extends DescentValue { + void show (AltosState state, int crc_errors) { + double speed = state.speed; + if (!state.ascent) + speed = state.baro_speed; + show("%6.0f m/s", speed); + } + public Speed (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Speed"); + } + } + + Speed speed; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %d° %9.6f", h, deg, min); + } + + class Lat extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + show(pos(state.gps.lat,"N", "S")); + else + show("???"); + } + public Lat (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends DescentValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + show(pos(state.gps.lon,"W", "E")); + else + show("???"); + } + public Lon (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Longitude"); + } + } + + Lon lon; + + class Apogee extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends DescentStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class Bearing extends DescentDualValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) { + show( String.format("%3.0f°", state.from_pad.bearing), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_LONG)); + } else { + show("???", "???"); + } + } + public Bearing (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Bearing"); + } + } + + Bearing bearing; + + class Range extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.range); + } + public Range (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Range"); + } + } + + Range range; + + class Elevation extends DescentValue { + void show (AltosState state, int crc_errors) { + show("%3.0f°", state.elevation); + } + public Elevation (GridBagLayout layout, int x, int y) { + super (layout, x, y, "Elevation"); + } + } + + Elevation elevation; + + public void reset() { + lat.reset(); + lon.reset(); + height.reset(); + speed.reset(); + bearing.reset(); + range.reset(); + elevation.reset(); + main.reset(); + apogee.reset(); + } + + public void show(AltosState state, int crc_errors) { + height.show(state, crc_errors); + speed.show(state, crc_errors); + bearing.show(state, crc_errors); + range.show(state, crc_errors); + elevation.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + main.show(state, crc_errors); + apogee.show(state, crc_errors); + } + + public AltosDescent() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in descent display */ + speed = new Speed(layout, 0, 0); + height = new Height(layout, 2, 0); + elevation = new Elevation(layout, 0, 1); + range = new Range(layout, 2, 1); + bearing = new Bearing(layout, 0, 2); + lat = new Lat(layout, 0, 3); + lon = new Lon(layout, 2, 3); + + apogee = new Apogee(layout, 4); + main = new Main(layout, 5); + } +} diff --git a/ao-tools/altosui/AltosDevice.java b/altosui/AltosDevice.java index f646305b..f0fda57b 100644 --- a/ao-tools/altosui/AltosDevice.java +++ b/altosui/AltosDevice.java @@ -101,6 +101,15 @@ public class AltosDevice extends altos_device { getName(), getSerial(), getPath()); } + public String toShortString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%s %d %s", + name, getSerial(), getPath()); + + } + public boolean isAltusMetrum() { if (getVendor() != vendor_altusmetrum) return false; diff --git a/ao-tools/altosui/AltosDeviceDialog.java b/altosui/AltosDeviceDialog.java index ec78e288..2966ad1e 100644 --- a/ao-tools/altosui/AltosDeviceDialog.java +++ b/altosui/AltosDeviceDialog.java @@ -26,7 +26,6 @@ import libaltosJNI.libaltos; import libaltosJNI.altos_device; import libaltosJNI.SWIGTYPE_p_altos_file; import libaltosJNI.SWIGTYPE_p_altos_list; -import altosui.AltosDevice; public class AltosDeviceDialog extends JDialog implements ActionListener { diff --git a/ao-tools/altosui/AltosDisplayThread.java b/altosui/AltosDisplayThread.java index 9cc3d5ce..3e719130 100644 --- a/ao-tools/altosui/AltosDisplayThread.java +++ b/altosui/AltosDisplayThread.java @@ -30,13 +30,18 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosDisplayThread extends Thread { - Frame parent; - IdleThread idle_thread; - AltosVoice voice; - String name; - int crc_errors; - AltosStatusTable flightStatus; - AltosInfoTable flightInfo; + Frame parent; + IdleThread idle_thread; + AltosVoice voice; + String name; + AltosFlightReader reader; + int crc_errors; + AltosFlightDisplay display; + + synchronized void show(AltosState state, int crc_errors) { + if (state != null) + display.show(state, crc_errors); + } class IdleThread extends Thread { @@ -64,8 +69,10 @@ public class AltosDisplayThread extends Thread { state.state < Altos.ao_flight_landed && state.range >= 0) { - voice.speak("Height %d, bearing %d, elevation %d, range %d.\n", + voice.speak("Height %d, bearing %s %d, elevation %d, range %d.\n", (int) (state.height + 0.5), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_VOICE), (int) (state.from_pad.bearing + 0.5), (int) (state.elevation + 0.5), (int) (state.range + 0.5)); @@ -93,6 +100,10 @@ public class AltosDisplayThread extends Thread { (int) (state.from_pad.bearing + 0.5), (int) (state.from_pad.distance + 0.5)); ++reported_landing; + if (state.state != Altos.ao_flight_landed) { + state.state = Altos.ao_flight_landed; + show(state, 0); + } } } @@ -105,10 +116,6 @@ public class AltosDisplayThread extends Thread { } public void run () { - - reported_landing = 0; - state = null; - report_interval = 10000; try { for (;;) { set_report_time(); @@ -148,15 +155,13 @@ public class AltosDisplayThread extends Thread { } else if (spoken) set_report_time(); } - } - - void init() { } - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } - void close(boolean interrupted) { } - - void update(AltosState state) throws InterruptedException { } + public IdleThread() { + state = null; + reported_landing = 0; + report_interval = 10000; + } + } boolean tell(AltosState state, AltosState old_state) { boolean ret = false; @@ -188,13 +193,6 @@ public class AltosDisplayThread extends Thread { return ret; } - void show(AltosState state, int crc_errors) { - if (state != null) { - flightStatus.set(state); - flightInfo.show(state, crc_errors); - } - } - public void run() { boolean interrupted = false; String line; @@ -204,16 +202,16 @@ public class AltosDisplayThread extends Thread { idle_thread = new IdleThread(); - flightInfo.clear(); + display.reset(); try { for (;;) { try { - AltosRecord record = read(); + AltosRecord record = reader.read(); if (record == null) break; old_state = state; state = new AltosState(record, state); - update(state); + reader.update(state); show(state, crc_errors); told = tell(state, old_state); idle_thread.notice(state, told); @@ -232,7 +230,9 @@ public class AltosDisplayThread extends Thread { "Telemetry Read Error", JOptionPane.ERROR_MESSAGE); } finally { - close(interrupted); + if (!interrupted) + idle_thread.report(true); + reader.close(interrupted); idle_thread.interrupt(); try { idle_thread.join(); @@ -240,16 +240,10 @@ public class AltosDisplayThread extends Thread { } } - public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosStatusTable in_status, AltosInfoTable in_info) { + public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) { parent = in_parent; voice = in_voice; - flightStatus = in_status; - flightInfo = in_info; + display = in_display; + reader = in_reader; } - - public void report() { - if (idle_thread != null) - idle_thread.report(true); - } - } diff --git a/ao-tools/altosui/AltosEepromDownload.java b/altosui/AltosEepromDownload.java index a7f64904..02fc36f2 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/altosui/AltosEepromDownload.java @@ -26,19 +26,7 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; +import java.util.concurrent.*; import libaltosJNI.*; @@ -90,7 +78,7 @@ public class AltosEepromDownload implements Runnable { Thread eeprom_thread; AltosEepromMonitor monitor; - void CaptureLog() throws IOException, InterruptedException { + void CaptureLog() throws IOException, InterruptedException, TimeoutException { int serial = 0; int block, state_block = 0; int addr; @@ -109,8 +97,10 @@ public class AltosEepromDownload implements Runnable { /* Pull the serial number out of the version information */ for (;;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(5000); + if (line == null) + throw new TimeoutException(); if (line.startsWith("serial-number")) { try { serial = Integer.parseInt(line.substring(13).trim()); @@ -137,7 +127,9 @@ public class AltosEepromDownload implements Runnable { any_valid = false; monitor.set_value(state_names[state], state, block - state_block); for (addr = 0; addr < 0x100;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(5000); + if (line == null) + throw new TimeoutException(); int[] values = ParseHex(line); if (values == null) { @@ -224,8 +216,7 @@ public class AltosEepromDownload implements Runnable { public void run () { if (remote) { - serial_line.set_channel(AltosPreferences.channel()); - serial_line.set_callsign(AltosPreferences.callsign()); + serial_line.set_radio(); serial_line.printf("p\nE 0\n"); serial_line.flush_input(); } @@ -240,10 +231,16 @@ public class AltosEepromDownload implements Runnable { CaptureLog(); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.getPath(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } catch (InterruptedException ie) { + } catch (TimeoutException te) { + JOptionPane.showMessageDialog(frame, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); } if (remote) serial_line.printf("~"); @@ -256,12 +253,11 @@ public class AltosEepromDownload implements Runnable { frame = given_frame; device = AltosDeviceDialog.show(frame, AltosDevice.product_any); - serial_line = new AltosSerial(); remote = false; if (device != null) { try { - serial_line.open(device); + serial_line = new AltosSerial(device); if (!device.matchProduct(AltosDevice.product_telemetrum)) remote = true; eeprom_thread = new Thread(this); @@ -269,12 +265,18 @@ public class AltosEepromDownload implements Runnable { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(frame, String.format("Cannot open device \"%s\"", - device.getPath()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.getPath(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosEepromIterable.java b/altosui/AltosEepromIterable.java index 2f1e7e90..f8e6d7e5 100644 --- a/ao-tools/altosui/AltosEepromIterable.java +++ b/altosui/AltosEepromIterable.java @@ -28,14 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosRecord; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - /* * AltosRecords with an index field so they can be sorted by tick while preserving * the original ordering for elements with matching ticks @@ -317,6 +309,12 @@ public class AltosEepromIterable extends AltosRecordIterable { int flags = (good.b >> 8); int seconds = hour * 3600 + minute * 60 + second; + /* Make sure this looks like a good GPS value */ + if ((flags & Altos.AO_GPS_NUM_SAT_MASK) >> Altos.AO_GPS_NUM_SAT_SHIFT < 4) + flags = (flags & ~Altos.AO_GPS_NUM_SAT_MASK) | (4 << Altos.AO_GPS_NUM_SAT_SHIFT); + flags |= Altos.AO_GPS_RUNNING; + flags |= Altos.AO_GPS_VALID; + int new_seconds = seconds + diff; if (new_seconds < 0) new_seconds += 24 * 3600; diff --git a/ao-tools/altosui/AltosEepromMonitor.java b/altosui/AltosEepromMonitor.java index b88fdd29..7ff00ead 100644 --- a/ao-tools/altosui/AltosEepromMonitor.java +++ b/altosui/AltosEepromMonitor.java @@ -28,16 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; - public class AltosEepromMonitor extends JDialog { Container pane; diff --git a/ao-tools/altosui/AltosEepromRecord.java b/altosui/AltosEepromRecord.java index 95cbe015..5a673817 100644 --- a/ao-tools/altosui/AltosEepromRecord.java +++ b/altosui/AltosEepromRecord.java @@ -28,17 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - public class AltosEepromRecord { public int cmd; public int tick; diff --git a/ao-tools/altosui/AltosFile.java b/altosui/AltosFile.java index 7f65381f..06360572 100644 --- a/ao-tools/altosui/AltosFile.java +++ b/altosui/AltosFile.java @@ -20,8 +20,6 @@ package altosui; import java.lang.*; import java.io.File; import java.util.*; -import altosui.AltosTelemetry; -import altosui.AltosPreferences; class AltosFile extends File { diff --git a/ao-tools/altosui/AltosFlash.java b/altosui/AltosFlash.java index a3e431cd..3af25c23 100644 --- a/ao-tools/altosui/AltosFlash.java +++ b/altosui/AltosFlash.java @@ -28,8 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosHexfile; - public class AltosFlash { File file; FileInputStream input; @@ -331,17 +329,16 @@ public class AltosFlash { return rom_config; } - public void open() throws IOException, FileNotFoundException, InterruptedException { + public AltosFlash(File in_file, AltosDevice in_debug_dongle) + throws IOException, FileNotFoundException, AltosSerialInUseException, InterruptedException { + file = in_file; + debug_dongle = in_debug_dongle; + debug = new AltosDebug(in_debug_dongle); input = new FileInputStream(file); image = new AltosHexfile(input); - debug.open(debug_dongle); - if (!debug.check_connection()) + if (!debug.check_connection()) { + debug.close(); throw new IOException("Debug port not connected"); - } - - public AltosFlash(File in_file, AltosDevice in_debug_dongle) { - file = in_file; - debug_dongle = in_debug_dongle; - debug = new AltosDebug(); + } } }
\ No newline at end of file diff --git a/ao-tools/altosui/AltosFlashUI.java b/altosui/AltosFlashUI.java index 86f57a5f..f63097ac 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/altosui/AltosFlashUI.java @@ -28,9 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosHexfile; -import altosui.AltosFlash; - public class AltosFlashUI extends JDialog implements Runnable, ActionListener @@ -68,10 +65,9 @@ public class AltosFlashUI } public void run() { - flash = new AltosFlash(file, debug_dongle); - flash.addActionListener(this); try { - flash.open(); + flash = new AltosFlash(file, debug_dongle); + flash.addActionListener(this); AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); romconfig_ui.set(flash.romconfig()); @@ -91,6 +87,12 @@ public class AltosFlashUI "Cannot open image", file.toString(), JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(frame, + String.format("Device \"%s\" already in use", + debug_dongle.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); } catch (IOException e) { JOptionPane.showMessageDialog(frame, e.getMessage(), diff --git a/altosui/AltosFlightDisplay.java b/altosui/AltosFlightDisplay.java new file mode 100644 index 00000000..d18d1d1f --- /dev/null +++ b/altosui/AltosFlightDisplay.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +public interface AltosFlightDisplay { + void reset(); + + void show(AltosState state, int crc_errors); +} diff --git a/ao-tools/altosui/AltosFlightInfoTableModel.java b/altosui/AltosFlightInfoTableModel.java index 2a22e3e5..e23eff68 100644 --- a/ao-tools/altosui/AltosFlightInfoTableModel.java +++ b/altosui/AltosFlightInfoTableModel.java @@ -29,53 +29,56 @@ import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; public class AltosFlightInfoTableModel extends AbstractTableModel { - private String[] columnNames = {"Field", "Value"}; + final static private String[] columnNames = {"Field", "Value"}; - class InfoLine { - String name; - String value; + int rows; + int cols; + private String[][] data; - public InfoLine(String n, String v) { - name = n; - value = v; - } - } - - private ArrayList<InfoLine> rows = new ArrayList<InfoLine>(); - - public int getColumnCount() { return columnNames.length; } - public String getColumnName(int col) { return columnNames[col]; } - - public int getRowCount() { return 20; } - - int current_row = 0; - int prev_num_rows = 0; + public int getColumnCount() { return cols; } + public int getRowCount() { return rows; } + public String getColumnName(int col) { return columnNames[col & 1]; } public Object getValueAt(int row, int col) { - if (row >= rows.size()) + if (row >= rows || col >= cols) return ""; - if (col == 0) - return rows.get(row).name; - else - return rows.get(row).value; + return data[row][col]; } - public void resetRow() { - current_row = 0; + int[] current_row; + + public void reset() { + for (int i = 0; i < cols / 2; i++) + current_row[i] = 0; } - public void addRow(String name, String value) { - if (current_row >= rows.size()) - rows.add(current_row, new InfoLine(name, value)); - else - rows.set(current_row, new InfoLine(name, value)); - current_row++; + + public void clear() { + reset(); + for (int c = 0; c < cols; c++) + for (int r = 0; r < rows; r++) + data[r][c] = ""; + fireTableDataChanged(); + } + + public void addRow(int col, String name, String value) { + if (current_row[col] < rows) { + data[current_row[col]][col * 2] = name; + data[current_row[col]][col * 2 + 1] = value; + } + current_row[col]++; } + public void finish() { - if (current_row > prev_num_rows) - fireTableRowsInserted(prev_num_rows, current_row - 1); - while (rows.size() > current_row) - rows.remove(rows.size() - 1); - prev_num_rows = current_row; + for (int c = 0; c < cols / 2; c++) + while (current_row[c] < rows) + addRow(c, "", ""); fireTableDataChanged(); } + + public AltosFlightInfoTableModel (int in_rows, int in_cols) { + rows = in_rows; + cols = in_cols * 2; + data = new String[rows][cols]; + current_row = new int[in_cols]; + } } diff --git a/altosui/AltosFlightReader.java b/altosui/AltosFlightReader.java new file mode 100644 index 00000000..3d59de9a --- /dev/null +++ b/altosui/AltosFlightReader.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.lang.*; +import java.text.*; +import java.io.*; + +public class AltosFlightReader { + String name; + + int serial; + + void init() { } + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; } + + void close(boolean interrupted) { } + + void set_channel(int channel) { } + + void update(AltosState state) throws InterruptedException { } +} diff --git a/altosui/AltosFlightStatus.java b/altosui/AltosFlightStatus.java new file mode 100644 index 00000000..59c9e9db --- /dev/null +++ b/altosui/AltosFlightStatus.java @@ -0,0 +1,154 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class FlightValue { + JLabel label; + JTextField value; + + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public FlightValue (GridBagLayout layout, int x, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(5, 5, 5, 5); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.status_font); + label.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(""); + value.setFont(Altos.status_font); + value.setHorizontalAlignment(SwingConstants.CENTER); + c.gridx = x; c.gridy = 1; + layout.setConstraints(value, c); + add(value); + } + } + + class Call extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.callsign); + } + public Call (GridBagLayout layout, int x) { + super (layout, x, "Callsign"); + } + } + + Call call; + + class Serial extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.serial)); + } + public Serial (GridBagLayout layout, int x) { + super (layout, x, "Serial"); + } + } + + Serial serial; + + class Flight extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.flight)); + } + public Flight (GridBagLayout layout, int x) { + super (layout, x, "Flight"); + } + } + + Flight flight; + + class FlightState extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(state.data.state()); + } + public FlightState (GridBagLayout layout, int x) { + super (layout, x, "State"); + } + } + + FlightState flight_state; + + class RSSI extends FlightValue { + void show(AltosState state, int crc_errors) { + value.setText(String.format("%d", state.data.rssi)); + } + public RSSI (GridBagLayout layout, int x) { + super (layout, x, "RSSI (dBm)"); + } + } + + RSSI rssi; + + public void reset () { + call.reset(); + serial.reset(); + flight.reset(); + flight_state.reset(); + rssi.reset(); + } + + public void show (AltosState state, int crc_errors) { + call.show(state, crc_errors); + serial.show(state, crc_errors); + flight.show(state, crc_errors); + flight_state.show(state, crc_errors); + rssi.show(state, crc_errors); + } + + public int height() { + Dimension d = layout.preferredLayoutSize(this); + return d.height; + } + + public AltosFlightStatus() { + layout = new GridBagLayout(); + + setLayout(layout); + + call = new Call(layout, 0); + serial = new Serial(layout, 1); + flight = new Flight(layout, 2); + flight_state = new FlightState(layout, 3); + rssi = new RSSI(layout, 4); + } +} diff --git a/ao-tools/altosui/AltosFlightStatusTableModel.java b/altosui/AltosFlightStatusTableModel.java index 4c24b6ac..4c24b6ac 100644 --- a/ao-tools/altosui/AltosFlightStatusTableModel.java +++ b/altosui/AltosFlightStatusTableModel.java diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java new file mode 100644 index 00000000..7fcfb8be --- /dev/null +++ b/altosui/AltosFlightUI.java @@ -0,0 +1,205 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosFlightUI extends JFrame implements AltosFlightDisplay { + String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; + Object[][] statusData = { { "0", "pad", "-50", "0" } }; + + AltosVoice voice; + AltosFlightReader reader; + AltosDisplayThread thread; + + JTabbedPane pane; + + AltosPad pad; + AltosAscent ascent; + AltosDescent descent; + AltosLanded landed; + AltosSiteMap sitemap; + + private AltosFlightStatus flightStatus; + private AltosInfoTable flightInfo; + + boolean exit_on_close = false; + + JComponent cur_tab = null; + JComponent which_tab(AltosState state) { + if (state.state < Altos.ao_flight_boost) + return pad; + if (state.state <= Altos.ao_flight_coast) + return ascent; + if (state.state <= Altos.ao_flight_main) + return descent; + return landed; + } + + void stop_display() { + if (thread != null && thread.isAlive()) { + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException ie) {} + } + thread = null; + } + + void disconnect() { + stop_display(); + } + + public void reset() { + pad.reset(); + ascent.reset(); + descent.reset(); + landed.reset(); + flightInfo.clear(); + sitemap.reset(); + } + + public void show(AltosState state, int crc_errors) { + JComponent tab = which_tab(state); + pad.show(state, crc_errors); + ascent.show(state, crc_errors); + descent.show(state, crc_errors); + landed.show(state, crc_errors); + if (tab != cur_tab) { + if (cur_tab == pane.getSelectedComponent()) { + pane.setSelectedComponent(tab); + } + cur_tab = tab; + } + flightStatus.show(state, crc_errors); + flightInfo.show(state, crc_errors); + sitemap.show(state, crc_errors); + } + + public void set_exit_on_close() { + exit_on_close = true; + } + + Container bag; + JComboBox channels; + + public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { + AltosPreferences.init(this); + + voice = in_voice; + reader = in_reader; + + bag = getContentPane(); + bag.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + + java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); + if (imgURL != null) + setIconImage(new ImageIcon(imgURL).getImage()); + + setTitle(String.format("AltOS %s", reader.name)); + + /* Stick channel selector at top of table for telemetry monitoring */ + if (serial >= 0) { + // Channel menu + channels = new AltosChannelMenu(AltosPreferences.channel(serial)); + channels.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int channel = channels.getSelectedIndex(); + reader.set_channel(channel); + } + }); + c.gridx = 0; + c.gridy = 0; + c.anchor = GridBagConstraints.WEST; + bag.add (channels, c); + } + + /* Flight status is always visible */ + flightStatus = new AltosFlightStatus(); + c.gridx = 0; + c.gridy = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + bag.add(flightStatus, c); + + /* The rest of the window uses a tabbed pane to + * show one of the alternate data views + */ + pane = new JTabbedPane(); + + pad = new AltosPad(); + pane.add("Launch Pad", pad); + + ascent = new AltosAscent(); + pane.add("Ascent", ascent); + + descent = new AltosDescent(); + pane.add("Descent", descent); + + landed = new AltosLanded(); + pane.add("Landed", landed); + + flightInfo = new AltosInfoTable(); + pane.add("Table", new JScrollPane(flightInfo)); + + sitemap = new AltosSiteMap(); + pane.add("Site Map", sitemap); + + /* Make the tabbed pane use the rest of the window space */ + c.gridx = 0; + c.gridy = 2; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + bag.add(pane, c); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + disconnect(); + setVisible(false); + dispose(); + if (exit_on_close) + System.exit(0); + } + }); + + pack(); + setVisible(true); + + thread = new AltosDisplayThread(this, voice, this, reader); + + thread.start(); + } + + public AltosFlightUI (AltosVoice in_voice, AltosFlightReader in_reader) { + this(in_voice, in_reader, -1); + } +} diff --git a/ao-tools/altosui/AltosGPS.java b/altosui/AltosGPS.java index acb6fb2c..83821842 100644 --- a/ao-tools/altosui/AltosGPS.java +++ b/altosui/AltosGPS.java @@ -19,8 +19,6 @@ package altosui; import java.lang.*; import java.text.*; -import altosui.AltosParse; - public class AltosGPS { public class AltosGPSSat { diff --git a/ao-tools/altosui/AltosGraph.java b/altosui/AltosGraph.java index fa3b87c1..58c27979 100644 --- a/ao-tools/altosui/AltosGraph.java +++ b/altosui/AltosGraph.java @@ -9,8 +9,6 @@ import java.io.*; import org.jfree.chart.JFreeChart; import org.jfree.chart.ChartUtilities; -import altosui.AltosDataPoint; - abstract class AltosGraph { public String filename; public abstract void addData(AltosDataPoint d); diff --git a/ao-tools/altosui/AltosGraphTime.java b/altosui/AltosGraphTime.java index ab01b888..a5451280 100644 --- a/ao-tools/altosui/AltosGraphTime.java +++ b/altosui/AltosGraphTime.java @@ -24,9 +24,6 @@ import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.TextAnchor; -import altosui.AltosDataPoint; -import altosui.AltosGraph; - class AltosGraphTime extends AltosGraph { static interface Element { void attachGraph(AltosGraphTime g); diff --git a/ao-tools/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index 25643c76..cd158651 100644 --- a/ao-tools/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -17,9 +17,6 @@ import org.jfree.chart.axis.AxisLocation; import org.jfree.ui.ApplicationFrame; import org.jfree.ui.RefineryUtilities; -import altosui.AltosDataPoint; -import altosui.AltosGraphTime; - public class AltosGraphUI extends JFrame { static final private Color red = new Color(194,31,31); @@ -154,18 +151,15 @@ public class AltosGraphUI extends JFrame } } - public AltosGraphUI(JFrame frame) - { - super("Altos Graph"); + public AltosGraphUI(AltosRecordIterable records) { + super("Altos Graph"); - AltosGraphDataChooser chooser; - chooser = new AltosGraphDataChooser(frame); - Iterable<AltosDataPoint> reader = chooser.runDialog(); - if (reader == null) - return; + Iterable<AltosDataPoint> reader = new AltosDataPointReader (records); + if (reader == null) + return; - init(reader, 0); - } + init(reader, 0); + } public AltosGraphUI(Iterable<AltosDataPoint> data, int which) { diff --git a/ao-tools/altosui/AltosGreatCircle.java b/altosui/AltosGreatCircle.java index 07c02c16..fb1b6ab3 100644 --- a/ao-tools/altosui/AltosGreatCircle.java +++ b/altosui/AltosGreatCircle.java @@ -17,8 +17,6 @@ package altosui; -import altosui.AltosGPS; - import java.lang.Math; public class AltosGreatCircle { @@ -30,6 +28,31 @@ public class AltosGreatCircle { static final double rad = Math.PI / 180; static final double earth_radius = 6371.2 * 1000; /* in meters */ + static int BEARING_LONG = 0; + static int BEARING_SHORT = 1; + static int BEARING_VOICE = 2; + String bearing_words(int length) { + String [][] bearing_string = { + { + "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" + }, { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }, { + "north", "nor nor east", "north east", "east nor east", + "east", "east sow east", "south east", "sow sow east", + "south", "sow sow west", "south west", "west sow west", + "west", "west nor west", "north west", "nor nor west " + } + }; + return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16]; + } + public AltosGreatCircle (double start_lat, double start_lon, double end_lat, double end_lon) { diff --git a/ao-tools/altosui/AltosHexfile.java b/altosui/AltosHexfile.java index 19e35ae1..19e35ae1 100644 --- a/ao-tools/altosui/AltosHexfile.java +++ b/altosui/AltosHexfile.java diff --git a/altosui/AltosIgnite.java b/altosui/AltosIgnite.java new file mode 100644 index 00000000..3cbd8a75 --- /dev/null +++ b/altosui/AltosIgnite.java @@ -0,0 +1,173 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.io.*; +import java.util.concurrent.*; + +public class AltosIgnite { + AltosDevice device; + AltosSerial serial; + boolean remote; + boolean serial_started; + final static int None = 0; + final static int Apogee = 1; + final static int Main = 2; + + final static int Unknown = 0; + final static int Ready = 1; + final static int Active = 2; + final static int Open = 3; + + private void start_serial() throws InterruptedException { + serial_started = true; + if (remote) { + serial.set_radio(); + serial.printf("p\nE 0\n"); + serial.flush_input(); + } + } + + private void stop_serial() throws InterruptedException { + if (!serial_started) + return; + serial_started = false; + if (serial == null) + return; + if (remote) { + serial.printf("~"); + serial.flush_output(); + } + } + + class string_ref { + String value; + + public String get() { + return value; + } + public void set(String i) { + value = i; + } + public string_ref() { + value = null; + } + } + + private boolean get_string(String line, String label, string_ref s) { + if (line.startsWith(label)) { + String quoted = line.substring(label.length()).trim(); + + if (quoted.startsWith("\"")) + quoted = quoted.substring(1); + if (quoted.endsWith("\"")) + quoted = quoted.substring(0,quoted.length()-1); + s.set(quoted); + return true; + } else { + return false; + } + } + + private int status(String status_name) { + if (status_name.equals("unknown")) + return Unknown; + if (status_name.equals("ready")) + return Ready; + if (status_name.equals("active")) + return Active; + if (status_name.equals("open")) + return Open; + return Unknown; + } + + public int status(int igniter) throws InterruptedException, TimeoutException { + int status = Unknown; + if (serial == null) + return status; + string_ref status_name = new string_ref(); + start_serial(); + serial.printf("t\n"); + for (;;) { + String line = serial.get_reply(5000); + if (line == null) + throw new TimeoutException(); + if (get_string(line, "Igniter: drogue Status: ", status_name)) + if (igniter == Apogee) + status = status(status_name.get()); + if (get_string(line, "Igniter: main Status: ", status_name)) { + if (igniter == Main) + status = status(status_name.get()); + break; + } + } + stop_serial(); + return status; + } + + public String status_string(int status) { + switch (status) { + case Unknown: return "Unknown"; + case Ready: return "Ready"; + case Active: return "Active"; + case Open: return "Open"; + default: return "Unknown"; + } + } + + public void fire(int igniter) { + if (serial == null) + return; + try { + start_serial(); + switch (igniter) { + case Main: + serial.printf("i DoIt main\n"); + break; + case Apogee: + serial.printf("i DoIt drogue\n"); + break; + } + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + } + + public void close() { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + serial.close(); + serial = null; + } + + public AltosIgnite(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + + device = in_device; + serial = new AltosSerial(device); + remote = false; + + if (!device.matchProduct(AltosDevice.product_telemetrum)) + remote = true; + } +}
\ No newline at end of file diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java new file mode 100644 index 00000000..d542729c --- /dev/null +++ b/altosui/AltosIgniteUI.java @@ -0,0 +1,317 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +public class AltosIgniteUI + extends JDialog + implements ActionListener +{ + AltosDevice device; + AltosIgnite ignite; + JFrame owner; + JLabel label; + JRadioButton apogee; + JLabel apogee_status_label; + JRadioButton main; + JLabel main_status_label; + JToggleButton arm; + JButton fire; + javax.swing.Timer timer; + + int apogee_status; + int main_status; + + final static int timeout = 1 * 1000; + + int time_remaining; + boolean timer_running; + + void set_arm_text() { + if (arm.isSelected()) + arm.setText(String.format("%d", time_remaining)); + else + arm.setText("Arm"); + } + + void start_timer() { + time_remaining = 10; + set_arm_text(); + timer_running = true; + } + + void stop_timer() { + time_remaining = 0; + arm.setSelected(false); + arm.setEnabled(false); + fire.setEnabled(false); + timer_running = false; + set_arm_text(); + } + + void cancel () { + apogee.setSelected(false); + main.setSelected(false); + fire.setEnabled(false); + stop_timer(); + } + + void get_ignite_status() throws InterruptedException, TimeoutException { + apogee_status = ignite.status(AltosIgnite.Apogee); + main_status = ignite.status(AltosIgnite.Main); + } + + void set_ignite_status() throws InterruptedException, TimeoutException { + get_ignite_status(); + apogee_status_label.setText(String.format("\"%s\"", ignite.status_string(apogee_status))); + main_status_label.setText(String.format("\"%s\"", ignite.status_string(main_status))); + } + + void close() { + timer.stop(); + setVisible(false); + ignite.close(); + } + + void abort() { + close(); + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toShortString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + } + + void tick_timer() { + if (timer_running) { + --time_remaining; + if (time_remaining <= 0) + cancel(); + else + set_arm_text(); + } + try { + set_ignite_status(); + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); + } + } + + void fire() { + if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) { + int igniter = AltosIgnite.None; + if (apogee.isSelected() && !main.isSelected()) + igniter = AltosIgnite.Apogee; + else if (main.isSelected() && !apogee.isSelected()) + igniter = AltosIgnite.Main; + ignite.fire(igniter); + cancel(); + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + if (cmd.equals("apogee") || cmd.equals("main")) { + stop_timer(); + } + + if (cmd.equals("apogee") && apogee.isSelected()) { + main.setSelected(false); + arm.setEnabled(true); + } + if (cmd.equals("main") && main.isSelected()) { + apogee.setSelected(false); + arm.setEnabled(true); + } + + if (cmd.equals("arm")) { + if (arm.isSelected()) { + fire.setEnabled(true); + start_timer(); + } else + cancel(); + } + if (cmd.equals("fire")) + fire(); + if (cmd.equals("tick")) + tick_timer(); + if (cmd.equals("close")) { + close(); + } + } + + /* A window listener to catch closing events and tell the config code */ + class ConfigListener extends WindowAdapter { + AltosIgniteUI ui; + + public ConfigListener(AltosIgniteUI this_ui) { + ui = this_ui; + } + + public void windowClosing(WindowEvent e) { + ui.actionPerformed(new ActionEvent(e.getSource(), + ActionEvent.ACTION_PERFORMED, + "close")); + } + } + + private boolean open() { + device = AltosDeviceDialog.show(owner, AltosDevice.product_any); + if (device != null) { + try { + ignite = new AltosIgnite(device); + return true; + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(owner, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(owner, + device.toShortString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + return false; + } + + public AltosIgniteUI(JFrame in_owner) { + + owner = in_owner; + apogee_status = AltosIgnite.Unknown; + main_status = AltosIgnite.Unknown; + + if (!open()) + return; + + Container pane = getContentPane(); + GridBagConstraints c = new GridBagConstraints(); + Insets i = new Insets(4,4,4,4); + + timer = new javax.swing.Timer(timeout, this); + timer.setActionCommand("tick"); + timer_running = false; + timer.restart(); + + owner = in_owner; + + pane.setLayout(new GridBagLayout()); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 1; + + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.anchor = GridBagConstraints.CENTER; + label = new JLabel ("Fire Igniter"); + pane.add(label, c); + + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + apogee = new JRadioButton ("Apogee"); + pane.add(apogee, c); + apogee.addActionListener(this); + apogee.setActionCommand("apogee"); + + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + apogee_status_label = new JLabel(); + pane.add(apogee_status_label, c); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + main = new JRadioButton ("Main"); + pane.add(main, c); + main.addActionListener(this); + main.setActionCommand("main"); + + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + main_status_label = new JLabel(); + pane.add(main_status_label, c); + + try { + set_ignite_status(); + } catch (InterruptedException ie) { + abort(); + return; + } catch (TimeoutException te) { + abort(); + return; + } + + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + arm = new JToggleButton ("Arm"); + pane.add(arm, c); + arm.addActionListener(this); + arm.setActionCommand("arm"); + arm.setEnabled(false); + + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; + fire = new JButton ("Fire"); + fire.setEnabled(false); + pane.add(fire, c); + fire.addActionListener(this); + fire.setActionCommand("fire"); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + + addWindowListener(new ConfigListener(this)); + } +}
\ No newline at end of file diff --git a/ao-tools/altosui/AltosInfoTable.java b/altosui/AltosInfoTable.java index 9964ab10..723f8301 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/altosui/AltosInfoTable.java @@ -28,14 +28,8 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosState; - -public class AltosInfoTable { - private Box box; - private JTable table[]; - private AltosFlightInfoTableModel model[]; - private Box ibox[]; +public class AltosInfoTable extends JTable { + private AltosFlightInfoTableModel model; private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); @@ -43,58 +37,35 @@ public class AltosInfoTable { static final int info_columns = 3; static final int info_rows = 17; - public AltosInfoTable() { - box = Box.createHorizontalBox(); - model = new AltosFlightInfoTableModel[info_columns]; - table = new JTable[info_columns]; - ibox = new Box[info_columns]; - for (int i = 0; i < info_columns; i++) { - model[i] = new AltosFlightInfoTableModel(); - table[i] = new JTable(model[i]); - ibox[i] = box.createVerticalBox(); - - table[i].setFont(infoValueFont); - table[i].setRowHeight(rowHeight()); - table[i].setShowGrid(true); - ibox[i].add(table[i].getTableHeader()); - ibox[i].add(table[i]); - box.add(ibox[i]); - } - } - - public int rowHeight() { - FontMetrics infoValueMetrics = table[0].getFontMetrics(infoValueFont); - return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 20 / 10; - } - - public int columnWidth() { - FontMetrics infoValueMetrics = table[0].getFontMetrics(infoValueFont); - return infoValueMetrics.charWidth('0') * 20 * 2; - } - - public int height() { - return rowHeight() * info_rows; + int desired_row_height() { + FontMetrics infoValueMetrics = getFontMetrics(infoValueFont); + return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10; } - public int width() { - return columnWidth() * info_columns; + public AltosInfoTable() { + super(new AltosFlightInfoTableModel(info_rows, info_columns)); + model = (AltosFlightInfoTableModel) getModel(); + setFont(infoValueFont); + setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + setShowGrid(true); + setRowHeight(desired_row_height()); + doLayout(); } - public Box box() { - return box; + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); } void info_reset() { - for (int i = 0; i < info_columns; i++) - model[i].resetRow(); + model.reset(); } void info_add_row(int col, String name, String value) { - model[col].addRow(name, value); + model.addRow(col, name, value); } void info_add_row(int col, String name, String format, Object... parameters) { - model[col].addRow(name, String.format(format, parameters)); + info_add_row (col, name, String.format(format, parameters)); } void info_add_deg(int col, String name, double v, int pos, int neg) { @@ -106,17 +77,15 @@ public class AltosInfoTable { double deg = Math.floor(v); double min = (v - deg) * 60; - model[col].addRow(name, String.format("%3.0f°%08.5f'", deg, min)); + info_add_row(col, name, String.format("%3.0f°%08.5f'", deg, min)); } void info_finish() { - for (int i = 0; i < info_columns; i++) - model[i].finish(); + model.finish(); } public void clear() { - info_reset(); - info_finish(); + model.clear(); } public void show(AltosState state, int crc_errors) { diff --git a/ao-tools/altosui/AltosKML.java b/altosui/AltosKML.java index d586033f..d586033f 100644 --- a/ao-tools/altosui/AltosKML.java +++ b/altosui/AltosKML.java diff --git a/altosui/AltosLanded.java b/altosui/AltosLanded.java new file mode 100644 index 00000000..d34efe6d --- /dev/null +++ b/altosui/AltosLanded.java @@ -0,0 +1,212 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLanded extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class LandedValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + + void show(String format, double v) { + value.setText(String.format(format, v)); + } + + public LandedValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.weightx = 0; + c.fill = GridBagConstraints.VERTICAL; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.weightx = 1; + c.fill = GridBagConstraints.BOTH; + layout.setConstraints(value, c); + add(value); + } + } + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class Lat extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lat,"N", "S")); + else + value.setText("???"); + } + public Lat (GridBagLayout layout, int y) { + super (layout, y, "Latitude"); + } + } + + Lat lat; + + class Lon extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.gps != null) + value.setText(pos(state.gps.lon,"E", "W")); + else + value.setText("???"); + } + public Lon (GridBagLayout layout, int y) { + super (layout, y, "Longitude"); + } + } + + Lon lon; + + class Bearing extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%3.0f°", state.from_pad.bearing); + else + value.setText("???"); + } + public Bearing (GridBagLayout layout, int y) { + super (layout, y, "Bearing"); + } + } + + Bearing bearing; + + class Distance extends LandedValue { + void show (AltosState state, int crc_errors) { + if (state.from_pad != null) + show("%6.0f m", state.from_pad.distance); + else + value.setText("???"); + } + public Distance (GridBagLayout layout, int y) { + super (layout, y, "Distance"); + } + } + + Distance distance; + + class Height extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m", state.max_height); + } + public Height (GridBagLayout layout, int y) { + super (layout, y, "Maximum Height"); + } + } + + Height height; + + class Speed extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s", state.max_speed); + } + public Speed (GridBagLayout layout, int y) { + super (layout, y, "Maximum Speed"); + } + } + + Speed speed; + + class Accel extends LandedValue { + void show (AltosState state, int crc_errors) { + show("%6.0f m/s²", state.max_acceleration); + } + public Accel (GridBagLayout layout, int y) { + super (layout, y, "Maximum Acceleration"); + } + } + + Accel accel; + + public void reset() { + lat.reset(); + lon.reset(); + bearing.reset(); + distance.reset(); + height.reset(); + speed.reset(); + accel.reset(); + } + + public void show(AltosState state, int crc_errors) { + bearing.show(state, crc_errors); + distance.show(state, crc_errors); + lat.show(state, crc_errors); + lon.show(state, crc_errors); + height.show(state, crc_errors); + speed.show(state, crc_errors); + accel.show(state, crc_errors); + } + + public AltosLanded() { + layout = new GridBagLayout(); + + label_font = new Font("Dialog", Font.PLAIN, 22); + value_font = new Font("Monospaced", Font.PLAIN, 22); + setLayout(layout); + + /* Elements in descent display */ + bearing = new Bearing(layout, 0); + distance = new Distance(layout, 1); + lat = new Lat(layout, 2); + lon = new Lon(layout, 3); + height = new Height(layout, 4); + speed = new Speed(layout, 5); + accel = new Accel(layout, 6); + } +} diff --git a/altosui/AltosLed.java b/altosui/AltosLed.java new file mode 100644 index 00000000..e08e9960 --- /dev/null +++ b/altosui/AltosLed.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLed extends JLabel { + ImageIcon on, off; + + ImageIcon create_icon(String path) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set(boolean set) { + if (set) + setIcon(on); + else + setIcon(off); + } + + public AltosLed(String on_path, String off_path) { + on = create_icon(on_path); + off = create_icon(off_path); + setIcon(off); + } +} diff --git a/altosui/AltosLights.java b/altosui/AltosLights.java new file mode 100644 index 00000000..2fa38412 --- /dev/null +++ b/altosui/AltosLights.java @@ -0,0 +1,73 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLights extends JComponent { + + GridBagLayout gridbag; + + AltosLed red, green; + + ImageIcon create_icon(String path, String description) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL, description); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set (boolean on) { + if (on) { + red.set(false); + green.set(true); + } else { + red.set(true); + green.set(false); + } + } + + public AltosLights() { + GridBagConstraints c; + gridbag = new GridBagLayout(); + setLayout(gridbag); + + c = new GridBagConstraints(); + red = new AltosLed("/redled.png", "/grayled.png"); + c.gridx = 0; c.gridy = 0; + c.insets = new Insets (0, 5, 0, 5); + gridbag.setConstraints(red, c); + add(red); + red.set(true); + green = new AltosLed("/greenled.png", "/grayled.png"); + c.gridx = 1; c.gridy = 0; + gridbag.setConstraints(green, c); + add(green); + green.set(false); + } +} diff --git a/ao-tools/altosui/AltosLine.java b/altosui/AltosLine.java index 86e9d4c6..86e9d4c6 100644 --- a/ao-tools/altosui/AltosLine.java +++ b/altosui/AltosLine.java diff --git a/ao-tools/altosui/AltosLog.java b/altosui/AltosLog.java index f876beba..dd147d21 100644 --- a/ao-tools/altosui/AltosLog.java +++ b/altosui/AltosLog.java @@ -22,9 +22,6 @@ import java.lang.*; import java.util.*; import java.text.ParseException; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosSerial; -import altosui.AltosFile; -import altosui.AltosLine; /* * This creates a thread to capture telemetry data and write it to @@ -39,9 +36,22 @@ class AltosLog implements Runnable { FileWriter log_file; Thread log_thread; - void close() throws IOException { - if (log_file != null) - log_file.close(); + private void close_log_file() { + if (log_file != null) { + try { + log_file.close(); + } catch (IOException io) { + } + log_file = null; + } + } + + void close() { + close_log_file(); + if (log_thread != null) { + log_thread.interrupt(); + log_thread = null; + } } boolean open (AltosTelemetry telem) throws IOException { @@ -71,7 +81,7 @@ class AltosLog implements Runnable { try { AltosTelemetry telem = new AltosTelemetry(line.line); if (telem.serial != serial || telem.flight != flight || log_file == null) { - close(); + close_log_file(); serial = telem.serial; flight = telem.flight; open(telem); @@ -89,10 +99,7 @@ class AltosLog implements Runnable { } catch (InterruptedException ie) { } catch (IOException ie) { } - try { - close(); - } catch (IOException ie) { - } + close(); } public AltosLog (AltosSerial s) { diff --git a/altosui/AltosPad.java b/altosui/AltosPad.java new file mode 100644 index 00000000..66954347 --- /dev/null +++ b/altosui/AltosPad.java @@ -0,0 +1,269 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosPad extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + + public class LaunchStatus { + JLabel label; + JTextField value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText(""); + lights.set(false); + } + + public LaunchStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.weighty = 1; + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + + } + } + + public class LaunchValue { + JLabel label; + JTextField value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText(""); + } + public LaunchValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.weighty = 1; + + label = new JLabel(text); + label.setFont(Altos.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + value = new JTextField(Altos.text_width); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + + class Battery extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.battery)); + lights.set(state.battery > 3.7); + } + public Battery (GridBagLayout layout, int y) { + super(layout, y, "Battery Voltage"); + } + } + + Battery battery; + + class Apogee extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class GPSLocked extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4d sats", state.gps.nsat)); + lights.set(state.gps.locked); + } + public GPSLocked (GridBagLayout layout, int y) { + super (layout, y, "GPS Locked"); + } + } + + GPSLocked gps_locked; + + class GPSReady extends LaunchStatus { + void show (AltosState state, int crc_errors) { + if (state.gps_ready) + value.setText("Ready"); + else + value.setText(String.format("Waiting %d", state.gps_waiting)); + lights.set(state.gps_ready); + } + public GPSReady (GridBagLayout layout, int y) { + super (layout, y, "GPS Ready"); + } + } + + GPSReady gps_ready; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class PadLat extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lat,"N", "S")); + } + public PadLat (GridBagLayout layout, int y) { + super (layout, y, "Pad Latitude"); + } + } + + PadLat pad_lat; + + class PadLon extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lon,"E", "W")); + } + public PadLon (GridBagLayout layout, int y) { + super (layout, y, "Pad Longitude"); + } + } + + PadLon pad_lon; + + class PadAlt extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.0f m", state.pad_alt)); + } + public PadAlt (GridBagLayout layout, int y) { + super (layout, y, "Pad Altitude"); + } + } + + PadAlt pad_alt; + + public void reset() { + battery.reset(); + apogee.reset(); + main.reset(); + gps_locked.reset(); + gps_ready.reset(); + pad_lat.reset(); + pad_lon.reset(); + pad_alt.reset(); + } + + public void show(AltosState state, int crc_errors) { + battery.show(state, crc_errors); + apogee.show(state, crc_errors); + main.show(state, crc_errors); + gps_locked.show(state, crc_errors); + gps_ready.show(state, crc_errors); + pad_lat.show(state, crc_errors); + pad_lon.show(state, crc_errors); + pad_alt.show(state, crc_errors); + } + + public AltosPad() { + layout = new GridBagLayout(); + + setLayout(layout); + + /* Elements in pad display: + * + * Battery voltage + * Igniter continuity + * GPS lock status + * GPS ready status + * GPS location + * Pad altitude + * RSSI + */ + battery = new Battery(layout, 0); + apogee = new Apogee(layout, 1); + main = new Main(layout, 2); + gps_locked = new GPSLocked(layout, 3); + gps_ready = new GPSReady(layout, 4); + pad_lat = new PadLat(layout, 5); + pad_lon = new PadLon(layout, 6); + pad_alt = new PadAlt(layout, 7); + } +} diff --git a/ao-tools/altosui/AltosParse.java b/altosui/AltosParse.java index 4d82de78..fbfcaaee 100644 --- a/ao-tools/altosui/AltosParse.java +++ b/altosui/AltosParse.java @@ -20,8 +20,6 @@ package altosui; import java.text.*; import java.lang.*; -import altosui.Altos; - public class AltosParse { static boolean isdigit(char c) { return '0' <= c && c <= '9'; diff --git a/ao-tools/altosui/AltosPreferences.java b/altosui/AltosPreferences.java index 52627563..e2a3df3b 100644 --- a/ao-tools/altosui/AltosPreferences.java +++ b/altosui/AltosPreferences.java @@ -32,7 +32,7 @@ class AltosPreferences { final static String logdirPreference = "LOGDIR"; /* channel preference name */ - final static String channelPreference = "CHANNEL"; + final static String channelPreferenceFormat = "CHANNEL-%d"; /* voice preference name */ final static String voicePreference = "VOICE"; @@ -52,8 +52,8 @@ class AltosPreferences { /* Log directory */ static File logdir; - /* Telemetry channel */ - static int channel; + /* Channel (map serial to channel) */ + static Hashtable<Integer, Integer> channels; /* Voice preference */ static boolean voice; @@ -80,7 +80,7 @@ class AltosPreferences { logdir.mkdirs(); } - channel = preferences.getInt(channelPreference, 0); + channels = new Hashtable<Integer,Integer>(); voice = preferences.getBoolean(voicePreference, true); @@ -151,15 +151,19 @@ class AltosPreferences { return logdir; } - public static void set_channel(int new_channel) { - channel = new_channel; + public static void set_channel(int serial, int new_channel) { + channels.put(serial, new_channel); synchronized (preferences) { - preferences.putInt(channelPreference, channel); + preferences.putInt(String.format(channelPreferenceFormat, serial), new_channel); flush_preferences(); } } - public static int channel() { + public static int channel(int serial) { + if (channels.containsKey(serial)) + return channels.get(serial); + int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0); + channels.put(serial, channel); return channel; } diff --git a/ao-tools/altosui/AltosReader.java b/altosui/AltosReader.java index 5be8795d..b9280a0c 100644 --- a/ao-tools/altosui/AltosReader.java +++ b/altosui/AltosReader.java @@ -21,8 +21,6 @@ import java.io.*; import java.util.*; import java.text.*; -import altosui.AltosRecord; - public class AltosReader { public AltosRecord read() throws IOException, ParseException { return null; } public void close() { } diff --git a/ao-tools/altosui/AltosRecord.java b/altosui/AltosRecord.java index 00484767..1160a273 100644 --- a/ao-tools/altosui/AltosRecord.java +++ b/altosui/AltosRecord.java @@ -21,8 +21,6 @@ import java.lang.*; import java.text.*; import java.util.HashMap; import java.io.*; -import altosui.AltosConvert; -import altosui.AltosGPS; public class AltosRecord { int version; diff --git a/ao-tools/altosui/AltosRecordIterable.java b/altosui/AltosRecordIterable.java index 147ecc14..a7df92d1 100644 --- a/ao-tools/altosui/AltosRecordIterable.java +++ b/altosui/AltosRecordIterable.java @@ -28,14 +28,6 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.AltosRecord; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosEepromMonitor; - public abstract class AltosRecordIterable implements Iterable<AltosRecord> { public abstract Iterator<AltosRecord> iterator(); public void write_comments(PrintStream out) { } diff --git a/ao-tools/altosui/AltosReplayThread.java b/altosui/AltosReplayReader.java index b418160a..4e5e1d93 100644 --- a/ao-tools/altosui/AltosReplayThread.java +++ b/altosui/AltosReplayReader.java @@ -28,33 +28,12 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosChannelMenu; -import altosui.AltosFlashUI; -import altosui.AltosLogfileChooser; -import altosui.AltosCSVUI; -import altosui.AltosLine; -import altosui.AltosStatusTable; -import altosui.AltosInfoTable; -import altosui.AltosDisplayThread; - /* * Open an existing telemetry file and replay it in realtime */ -public class AltosReplayThread extends AltosDisplayThread { +public class AltosReplayReader extends AltosFlightReader { Iterator<AltosRecord> iterator; - String name; public AltosRecord read() { if (iterator.hasNext()) @@ -63,8 +42,6 @@ public class AltosReplayThread extends AltosDisplayThread { } public void close (boolean interrupted) { - if (!interrupted) - report(); } void update(AltosState state) throws InterruptedException { @@ -73,10 +50,7 @@ public class AltosReplayThread extends AltosDisplayThread { Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); } - public AltosReplayThread(Frame parent, Iterator<AltosRecord> in_iterator, - String in_name, AltosVoice voice, - AltosStatusTable status, AltosInfoTable info) { - super(parent, voice, status, info); + public AltosReplayReader(Iterator<AltosRecord> in_iterator, String in_name) { iterator = in_iterator; name = in_name; } diff --git a/ao-tools/altosui/AltosRomconfig.java b/altosui/AltosRomconfig.java index 22d2dbd3..55056b5e 100644 --- a/ao-tools/altosui/AltosRomconfig.java +++ b/altosui/AltosRomconfig.java @@ -17,7 +17,6 @@ package altosui; import java.io.*; -import altosui.AltosHexfile; public class AltosRomconfig { public boolean valid; diff --git a/ao-tools/altosui/AltosRomconfigUI.java b/altosui/AltosRomconfigUI.java index 2134975d..e1dc974e 100644 --- a/ao-tools/altosui/AltosRomconfigUI.java +++ b/altosui/AltosRomconfigUI.java @@ -28,8 +28,6 @@ import java.util.*; import java.text.*; import java.util.prefs.*; -import altosui.AltosRomconfig; - public class AltosRomconfigUI extends JDialog implements ActionListener diff --git a/ao-tools/altosui/AltosSerial.java b/altosui/AltosSerial.java index a1fc4371..b19143e5 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/altosui/AltosSerial.java @@ -23,16 +23,10 @@ package altosui; import java.lang.*; import java.io.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.LinkedList; -import java.util.Iterator; -import altosui.AltosSerialMonitor; -import altosui.AltosLine; -import libaltosJNI.libaltos; -import libaltosJNI.altos_device; -import libaltosJNI.SWIGTYPE_p_altos_file; -import libaltosJNI.SWIGTYPE_p_altos_list; -import libaltosJNI.libaltosConstants; +import java.util.concurrent.*; +import java.util.*; + +import libaltosJNI.*; /* * This class reads from the serial port and places each received @@ -42,6 +36,9 @@ import libaltosJNI.libaltosConstants; public class AltosSerial implements Runnable { + static List<String> devices_opened = Collections.synchronizedList(new LinkedList<String>()); + + AltosDevice device; SWIGTYPE_p_altos_file altos; LinkedList<LinkedBlockingQueue<AltosLine>> monitors; LinkedBlockingQueue<AltosLine> reply_queue; @@ -117,16 +114,20 @@ public class AltosSerial implements Runnable { public void flush_input() { flush_output(); - try { - Thread.sleep(200); - } catch (InterruptedException ie) { - } - synchronized(this) { - if (!"VERSION".startsWith(line) && - !line.startsWith("VERSION")) - line = ""; - reply_queue.clear(); - } + boolean got_some; + do { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + got_some = !reply_queue.isEmpty(); + synchronized(this) { + if (!"VERSION".startsWith(line) && + !line.startsWith("VERSION")) + line = ""; + reply_queue.clear(); + } + } while (got_some); } public String get_reply() throws InterruptedException { @@ -135,6 +136,14 @@ public class AltosSerial implements Runnable { return line.line; } + public String get_reply(int timeout) throws InterruptedException { + flush_output(); + AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS); + if (line == null) + return null; + return line.line; + } + public void add_monitor(LinkedBlockingQueue<AltosLine> q) { set_monitor(true); monitors.add(q); @@ -146,10 +155,6 @@ public class AltosSerial implements Runnable { set_monitor(false); } - public boolean opened() { - return altos != null; - } - public void close() { if (altos != null) { libaltos.altos_close(altos); @@ -166,6 +171,9 @@ public class AltosSerial implements Runnable { libaltos.altos_free(altos); altos = null; } + synchronized (devices_opened) { + devices_opened.remove(device.getPath()); + } } public void putc(char c) { @@ -183,16 +191,27 @@ public class AltosSerial implements Runnable { print(String.format(format, arguments)); } - public void open(altos_device device) throws FileNotFoundException { - close(); + private void open() throws FileNotFoundException, AltosSerialInUseException { + synchronized (devices_opened) { + if (devices_opened.contains(device.getPath())) + throw new AltosSerialInUseException(device); + devices_opened.add(device.getPath()); + } altos = libaltos.altos_open(device); - if (altos == null) - throw new FileNotFoundException(device.getPath()); + if (altos == null) { + close(); + throw new FileNotFoundException(device.toShortString()); + } input_thread = new Thread(this); input_thread.start(); print("~\nE 0\n"); + set_monitor(false); flush_output(); - set_monitor(monitor_mode); + } + + public void set_radio() { + set_channel(AltosPreferences.channel(device.getSerial())); + set_callsign(AltosPreferences.callsign()); } public void set_channel(int channel) { @@ -223,12 +242,12 @@ public class AltosSerial implements Runnable { } } - public AltosSerial() { - altos = null; - input_thread = null; + public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + device = in_device; line = ""; monitor_mode = false; monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> (); reply_queue = new LinkedBlockingQueue<AltosLine> (); + open(); } } diff --git a/altosui/AltosSerialInUseException.java b/altosui/AltosSerialInUseException.java new file mode 100644 index 00000000..4b108c7c --- /dev/null +++ b/altosui/AltosSerialInUseException.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import libaltosJNI.*; + +public class AltosSerialInUseException extends Exception { + public altos_device device; + + public AltosSerialInUseException (altos_device in_device) { + device = in_device; + } +} diff --git a/ao-tools/altosui/AltosSerialMonitor.java b/altosui/AltosSerialMonitor.java index ad0e9295..ad0e9295 100644 --- a/ao-tools/altosui/AltosSerialMonitor.java +++ b/altosui/AltosSerialMonitor.java diff --git a/altosui/AltosSiteMap.java b/altosui/AltosSiteMap.java new file mode 100644 index 00000000..d4a4cbf4 --- /dev/null +++ b/altosui/AltosSiteMap.java @@ -0,0 +1,391 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { + // preferred vertical step in a tile in naut. miles + // will actually choose a step size between x and 2x, where this + // is 1.5x + static final double tile_size_nmi = 0.75; + + static final int px_size = 512; + + static final int MAX_TILE_DELTA = 100; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + HashMap<Point,AltosSiteMapTile> mapTiles = new HashMap<Point,AltosSiteMapTile>(); + Point2D.Double centre; + + private Point2D.Double tileCoordOffset(Point p) { + return new Point2D.Double(centre.x - p.x*px_size, + centre.y - p.y * px_size); + } + + private Point tileOffset(Point2D.Double p) { + return new Point((int)Math.floor((centre.x+p.x)/px_size), + (int)Math.floor((centre.y+p.y)/px_size)); + } + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi*4/3/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + + public void reset() { + // nothing + } + + private void bgLoadMap(final AltosSiteMapTile tile, + final File pngfile, final String pngurl) + { + //System.out.printf("Loading/fetching map %s\n", pngfile); + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + tile.loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + asm.centre = asm.getBaseLocation(lat, lng); + + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -asm.centre.x + x*px_size + px_size/2, + -asm.centre.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + } + } + + private void initMap(AltosSiteMapTile tile, Point offset) { + Point2D.Double coord = tileCoordOffset(offset); + + LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(tile, pngfile, pngurl); + } + + private void initMaps(double lat, double lng) { + centre = getBaseLocation(lat, lng); + + for (Point k : mapTiles.keySet()) { + initMap(mapTiles.get(k), k); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + Point2D.Double last_pt = null; + int last_state = -1; + public void show(final AltosState state, final int crc_errors) { + // if insufficient gps data, nothing to update + if (state.gps == null) + return; + if (!state.gps.locked && state.gps.nsat < 4) + return; + + if (!initialised) { + if (state.pad_lat != 0 || state.pad_lon != 0) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } else if (state.gps.lat != 0 || state.gps.lon != 0) { + initMaps(state.gps.lat, state.gps.lon); + initialised = true; + } else { + return; + } + } + + final Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt == pt && last_state == state.state) + return; + + if (last_pt == null) { + last_pt = pt; + } + boolean in_any = false; + for (Point offset : mapTiles.keySet()) { + AltosSiteMapTile tile = mapTiles.get(offset); + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); + tile.show(state, crc_errors, lref, ref); + if (0 <= ref.x && ref.x < px_size) + if (0 <= ref.y && ref.y < px_size) + in_any = true; + } + + Point offset = tileOffset(pt); + if (!in_any) { + Point2D.Double ref, lref; + ref = translatePoint(pt, tileCoordOffset(offset)); + lref = translatePoint(last_pt, tileCoordOffset(offset)); + + AltosSiteMapTile tile = createTile(offset); + tile.show(state, crc_errors, lref, ref); + initMap(tile, offset); + finishTileLater(tile, offset); + } + + scrollRocketToVisible(pt); + + if (offset != tileOffset(last_pt)) { + ensureTilesAround(offset); + } + + last_pt = pt; + last_state = state.state; + } + + private AltosSiteMapTile createTile(Point offset) { + AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + mapTiles.put(offset, tile); + return tile; + } + private void finishTileLater(final AltosSiteMapTile tile, + final Point offset) + { + SwingUtilities.invokeLater( new Runnable() { + public void run() { + addTileAt(tile, offset); + } + } ); + } + + private void ensureTilesAround(Point base_offset) { + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + Point offset = new Point(base_offset.x + x, base_offset.y + y); + if (mapTiles.containsKey(offset)) + continue; + AltosSiteMapTile tile = createTile(offset); + initMap(tile, offset); + finishTileLater(tile, offset); + } + } + } + + private Point topleft = new Point(0,0); + private void scrollRocketToVisible(Point2D.Double pt) { + Rectangle r = comp.getVisibleRect(); + Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); + int dx = (int)copt.x - r.width/2 - r.x; + int dy = (int)copt.y - r.height/2 - r.y; + if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) { + r.x += dx; + r.y += dy; + comp.scrollRectToVisible(r); + } + } + + private void addTileAt(AltosSiteMapTile tile, Point offset) { + if (Math.abs(offset.x) >= MAX_TILE_DELTA || + Math.abs(offset.y) >= MAX_TILE_DELTA) + { + System.out.printf("Rocket too far away from pad (tile %d,%d)\n", + offset.x, offset.y); + return; + } + + boolean review = false; + Rectangle r = comp.getVisibleRect(); + if (offset.x < topleft.x) { + r.x += (topleft.x - offset.x) * px_size; + topleft.x = offset.x; + review = true; + } + if (offset.y < topleft.y) { + r.y += (topleft.y - offset.y) * px_size; + topleft.y = offset.y; + review = true; + } + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); + + c.gridx = offset.x + MAX_TILE_DELTA; + c.gridy = offset.y + MAX_TILE_DELTA; + layout.setConstraints(tile, c); + + comp.add(tile); + if (review) { + comp.scrollRectToVisible(r); + } + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); + } + } + + JComponent comp = new JComponent() { }; + private GridBagLayout layout = new GridBagLayout(); + + public AltosSiteMap() { + GrabNDrag scroller = new GrabNDrag(comp); + + comp.setLayout(layout); + + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + Point offset = new Point(x, y); + AltosSiteMapTile t = createTile(offset); + addTileAt(t, offset); + } + } + setViewportView(comp); + setPreferredSize(new Dimension(500,200)); + } +} diff --git a/altosui/AltosSiteMapCache.java b/altosui/AltosSiteMapCache.java new file mode 100644 index 00000000..2e62cc45 --- /dev/null +++ b/altosui/AltosSiteMapCache.java @@ -0,0 +1,103 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapCache extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } + + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } +} diff --git a/altosui/AltosSiteMapTile.java b/altosui/AltosSiteMapTile.java new file mode 100644 index 00000000..8301f42b --- /dev/null +++ b/altosui/AltosSiteMapTile.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMapTile extends JLayeredPane { + JLabel mapLabel; + JLabel draw; + Graphics2D g2d; + + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + private boolean drawn_landed_circle = false; + private boolean drawn_boost_circle = false; + public synchronized void show(AltosState state, int crc_errors, + Point2D.Double last_pt, Point2D.Double pt) + { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + + if (state.state == 3 && !drawn_boost_circle) { + drawn_boost_circle = true; + g2d.setColor(Color.RED); + g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); + g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); + g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); + } + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + g2d.setColor(Color.BLACK); + g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + repaint(); + } + + public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { + BufferedImage img = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + g.setColor(c); + g.fillRect(0, 0, px_size, px_size); + l.setIcon(new ImageIcon(img)); + return g; + } + + public AltosSiteMapTile(int px_size) { + setPreferredSize(new Dimension(px_size, px_size)); + + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY, px_size); + mapLabel.setOpaque(true); + mapLabel.setBounds(0, 0, px_size, px_size); + add(mapLabel, new Integer(0)); + + draw = new JLabel(); + g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); + } +} diff --git a/ao-tools/altosui/AltosState.java b/altosui/AltosState.java index 1048bb51..ec499d5a 100644 --- a/ao-tools/altosui/AltosState.java +++ b/altosui/AltosState.java @@ -21,9 +21,6 @@ package altosui; -import altosui.AltosRecord; -import altosui.AltosGPS; - public class AltosState { AltosRecord data; @@ -35,6 +32,7 @@ public class AltosState { int tick; int state; + boolean landed; boolean ascent; /* going up? */ double ground_altitude; diff --git a/ao-tools/altosui/AltosTelemetry.java b/altosui/AltosTelemetry.java index be22dac6..bdb6466a 100644 --- a/ao-tools/altosui/AltosTelemetry.java +++ b/altosui/AltosTelemetry.java @@ -20,10 +20,6 @@ package altosui; import java.lang.*; import java.text.*; import java.util.HashMap; -import altosui.AltosConvert; -import altosui.AltosRecord; -import altosui.AltosGPS; -import altosui.AltosCRCException; /* * Telemetry data contents diff --git a/ao-tools/altosui/AltosTelemetryIterable.java b/altosui/AltosTelemetryIterable.java index 0a125c98..a71ab872 100644 --- a/ao-tools/altosui/AltosTelemetryIterable.java +++ b/altosui/AltosTelemetryIterable.java @@ -20,7 +20,6 @@ package altosui; import java.io.*; import java.util.*; import java.text.*; -import altosui.AltosTelemetry; public class AltosTelemetryIterable extends AltosRecordIterable { LinkedList<AltosRecord> records; diff --git a/altosui/AltosTelemetryReader.java b/altosui/AltosTelemetryReader.java new file mode 100644 index 00000000..6c5a9397 --- /dev/null +++ b/altosui/AltosTelemetryReader.java @@ -0,0 +1,61 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.lang.*; +import java.text.*; +import java.io.*; +import java.util.concurrent.*; + +class AltosTelemetryReader extends AltosFlightReader { + AltosDevice device; + AltosSerial serial; + AltosLog log; + + LinkedBlockingQueue<AltosLine> telem; + + AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { + AltosLine l = telem.take(); + if (l.line == null) + throw new IOException("IO error"); + return new AltosTelemetry(l.line); + } + + void close(boolean interrupted) { + serial.remove_monitor(telem); + log.close(); + serial.close(); + } + + void set_channel(int channel) { + serial.set_channel(channel); + AltosPreferences.set_channel(device.getSerial(), channel); + } + + public AltosTelemetryReader (AltosDevice in_device) + throws FileNotFoundException, AltosSerialInUseException, IOException { + device = in_device; + serial = new AltosSerial(device); + log = new AltosLog(serial); + name = device.toShortString(); + + telem = new LinkedBlockingQueue<AltosLine>(); + serial.set_radio(); + serial.add_monitor(telem); + } +} diff --git a/ao-tools/altosui/AltosUI.app/Contents/Info.plist b/altosui/AltosUI.app/Contents/Info.plist index 97b1b59c..97b1b59c 100644 --- a/ao-tools/altosui/AltosUI.app/Contents/Info.plist +++ b/altosui/AltosUI.app/Contents/Info.plist diff --git a/ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub Binary files differindex c661d3e1..c661d3e1 100755 --- a/ao-tools/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub +++ b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub diff --git a/ao-tools/altosui/AltosUI.app/Contents/PkgInfo b/altosui/AltosUI.app/Contents/PkgInfo index 8a43480f..8a43480f 100644 --- a/ao-tools/altosui/AltosUI.app/Contents/PkgInfo +++ b/altosui/AltosUI.app/Contents/PkgInfo diff --git a/ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns Binary files differindex fe49f362..fe49f362 100644 --- a/ao-tools/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns +++ b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java new file mode 100644 index 00000000..94c4dd2a --- /dev/null +++ b/altosui/AltosUI.java @@ -0,0 +1,405 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +import libaltosJNI.*; + +public class AltosUI extends JFrame { + public AltosVoice voice = new AltosVoice(); + + public static boolean load_library(Frame frame) { + if (!AltosDevice.load_library()) { + JOptionPane.showMessageDialog(frame, + String.format("No AltOS library in \"%s\"", + System.getProperty("java.library.path","<undefined>")), + "Cannot load device access library", + JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + void telemetry_window(AltosDevice device) { + try { + AltosFlightReader reader = new AltosTelemetryReader(device); + if (reader != null) + new AltosFlightUI(voice, reader, device.getSerial()); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Cannot open device \"%s\"", + device.toShortString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(AltosUI.this, + String.format("Device \"%s\" already in use", + device.toShortString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(AltosUI.this, + device.toShortString(), + "Unkonwn I/O error", + JOptionPane.ERROR_MESSAGE); + } + } + + Container pane; + GridBagLayout gridbag; + + JButton addButton(int x, int y, String label) { + GridBagConstraints c; + JButton b; + + c = new GridBagConstraints(); + c.gridx = x; c.gridy = y; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + b = new JButton(label); + + Dimension ps = b.getPreferredSize(); + + gridbag.setConstraints(b, c); + add(b, c); + return b; + } + + public AltosUI() { + + load_library(null); + + java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); + if (imgURL != null) + setIconImage(new ImageIcon(imgURL).getImage()); + + AltosPreferences.init(this); + + pane = getContentPane(); + gridbag = new GridBagLayout(); + pane.setLayout(gridbag); + + JButton b; + + b = addButton(0, 0, "Monitor Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConnectToDevice(); + } + }); + b = addButton(1, 0, "Save Flight Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SaveFlightData(); + } + }); + b = addButton(2, 0, "Replay Flight"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Replay(); + } + }); + b = addButton(3, 0, "Graph Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + GraphData(); + } + }); + b = addButton(4, 0, "Export Data"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ExportData(); + } + }); + b = addButton(0, 1, "Configure TeleMetrum"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureTeleMetrum(); + } + }); + + b = addButton(1, 1, "Configure AltosUI"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ConfigureAltosUI(); + } + }); + + b = addButton(2, 1, "Flash Image"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FlashImage(); + } + }); + + b = addButton(3, 1, "Fire Igniter"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FireIgniter(); + } + }); + + b = addButton(4, 1, "Quit"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); + + setTitle("AltOS"); + + pane.doLayout(); + pane.validate(); + + doLayout(); + validate(); + + setVisible(true); + + Insets i = getInsets(); + Dimension ps = rootPane.getPreferredSize(); + ps.width += i.left + i.right; + ps.height += i.top + i.bottom; + setPreferredSize(ps); + setSize(ps); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + + private void ConnectToDevice() { + AltosDevice device = AltosDeviceDialog.show(AltosUI.this, + AltosDevice.product_basestation); + + if (device != null) + telemetry_window(device); + } + + void ConfigureCallsign() { + String result; + result = JOptionPane.showInputDialog(AltosUI.this, + "Configure Callsign", + AltosPreferences.callsign()); + if (result != null) + AltosPreferences.set_callsign(result); + } + + void ConfigureTeleMetrum() { + new AltosConfig(AltosUI.this); + } + + void FlashImage() { + new AltosFlashUI(AltosUI.this); + } + + void FireIgniter() { + new AltosIgniteUI(AltosUI.this); + } + + /* + * Replay a flight from telemetry data + */ + private void Replay() { + AltosDataChooser chooser = new AltosDataChooser( + AltosUI.this); + + AltosRecordIterable iterable = chooser.runDialog(); + if (iterable != null) { + AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), + chooser.filename()); + new AltosFlightUI(voice, reader); + } + } + + /* Connect to TeleMetrum, either directly or through + * a TeleDongle over the packet link + */ + private void SaveFlightData() { + new AltosEepromDownload(AltosUI.this); + } + + /* Load a flight log file and write out a CSV file containing + * all of the data in standard units + */ + + private void ExportData() { + AltosDataChooser chooser; + chooser = new AltosDataChooser(this); + AltosRecordIterable record_reader = chooser.runDialog(); + if (record_reader == null) + return; + new AltosCSVUI(AltosUI.this, record_reader, chooser.file()); + } + + /* Load a flight log CSV file and display a pretty graph. + */ + + private void GraphData() { + AltosDataChooser chooser; + chooser = new AltosDataChooser(this); + AltosRecordIterable record_reader = chooser.runDialog(); + if (record_reader == null) + return; + new AltosGraphUI(record_reader); + } + + private void ConfigureAltosUI() { + new AltosConfigureUI(AltosUI.this, voice); + } + + static AltosRecordIterable open_logfile(String filename) { + File file = new File (filename); + try { + FileInputStream in; + + in = new FileInputStream(file); + if (filename.endsWith("eeprom")) + return new AltosEepromIterable(in); + else + return new AltosTelemetryIterable(in); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static AltosWriter open_csv(String filename) { + File file = new File (filename); + try { + return new AltosCSV(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static AltosWriter open_kml(String filename) { + File file = new File (filename); + try { + return new AltosKML(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static final int process_csv = 1; + static final int process_kml = 2; + + static void process_file(String input, int process) { + AltosRecordIterable iterable = open_logfile(input); + if (iterable == null) + return; + if (process == 0) + process = process_csv; + if ((process & process_csv) != 0) { + String output = Altos.replace_extension(input,".csv"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_csv(output); + if (writer != null) { + writer.write(iterable); + writer.close(); + } + } + } + if ((process & process_kml) != 0) { + String output = Altos.replace_extension(input,".kml"); + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + } else { + AltosWriter writer = open_kml(output); + if (writer == null) + return; + writer.write(iterable); + writer.close(); + } + } + } + + public static void main(final String[] args) { + int process = 0; + /* Handle batch-mode */ + if (args.length == 1 && args[0].equals("--help")) { + System.out.printf("Usage: altosui [OPTION]... [FILE]...\n"); + System.out.printf(" Options:\n"); + System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n"); + System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n"); + System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n"); + System.out.printf(" --kml\tgenerate KML output for use with Google Earth\n"); + } else if (args.length == 3 && args[0].equals("--fetchmaps")) { + double lat = Double.parseDouble(args[1]); + double lon = Double.parseDouble(args[2]); + AltosSiteMap.prefetchMaps(lat, lon, 5, 5); + } else if (args.length == 2 && args[0].equals("--replay")) { + String filename = args[1]; + FileInputStream in; + try { + in = new FileInputStream(filename); + } catch (Exception e) { + System.out.printf("Failed to open file '%s'\n", filename); + return; + } + AltosRecordIterable recs; + AltosReplayReader reader; + if (filename.endsWith("eeprom")) { + recs = new AltosEepromIterable(in); + } else { + recs = new AltosTelemetryIterable(in); + } + reader = new AltosReplayReader(recs.iterator(), filename); + AltosFlightUI flight_ui = new AltosFlightUI(new AltosVoice(), reader); + flight_ui.set_exit_on_close(); + return; + } else if (args.length > 0) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals("--kml")) + process |= process_kml; + else if (args[i].equals("--csv")) + process |= process_csv; + else + process_file(args[i], process); + } + } else { + AltosUI altosui = new AltosUI(); + altosui.setVisible(true); + + AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); + for (int i = 0; i < devices.length; i++) + altosui.telemetry_window(devices[i]); + } + } +} diff --git a/ao-tools/altosui/AltosVoice.java b/altosui/AltosVoice.java index ac13ee14..ac13ee14 100644 --- a/ao-tools/altosui/AltosVoice.java +++ b/altosui/AltosVoice.java diff --git a/ao-tools/altosui/AltosWriter.java b/altosui/AltosWriter.java index a172dff0..a172dff0 100644 --- a/ao-tools/altosui/AltosWriter.java +++ b/altosui/AltosWriter.java diff --git a/altosui/GrabNDrag.java b/altosui/GrabNDrag.java new file mode 100644 index 00000000..e6b87b58 --- /dev/null +++ b/altosui/GrabNDrag.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; + +class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent scroll) { + this.scroll = scroll; + scroll.addMouseMotionListener(this); + scroll.addMouseListener(this); + scroll.setAutoscrolls(true); + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } +} diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi b/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi index 3ed821eb..3ed821eb 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe Binary files differindex 615bae15..615bae15 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c index efe866e9..efe866e9 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp index 874e66c7..874e66c7 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw index b3d02f0e..b3d02f0e 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt b/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt index e5877aa6..e5877aa6 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf index ccda1d87..ccda1d87 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf diff --git a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys Binary files differindex 7882583b..7882583b 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys +++ b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys diff --git a/ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll Binary files differindex 482e955e..482e955e 100644 --- a/ao-tools/altosui/Instdrv/NSIS/Plugins/InstDrv.dll +++ b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll diff --git a/ao-tools/altosui/Makefile-standalone b/altosui/Makefile-standalone index a95a5aa8..a95a5aa8 100644 --- a/ao-tools/altosui/Makefile-standalone +++ b/altosui/Makefile-standalone diff --git a/ao-tools/altosui/Makefile.am b/altosui/Makefile.am index 2f4ed6d8..e2ff55af 100644 --- a/ao-tools/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -1,3 +1,4 @@ +SUBDIRS=libaltos JAVAROOT=classes AM_JAVACFLAGS=-encoding UTF-8 @@ -5,19 +6,23 @@ man_MANS=altosui.1 altoslibdir=$(libdir)/altos -CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/*:/usr/share/java/*" +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:libaltos:$(FREETTS)/*:/usr/share/java/*" bin_SCRIPTS=altosui altosui_JAVA = \ + GrabNDrag.java \ + AltosAscent.java \ AltosChannelMenu.java \ AltosConfig.java \ AltosConfigUI.java \ + AltosConfigureUI.java \ AltosConvert.java \ AltosCRCException.java \ AltosCSV.java \ AltosCSVUI.java \ AltosDebug.java \ + AltosDescent.java \ AltosDeviceDialog.java \ AltosDevice.java \ AltosDisplayThread.java \ @@ -28,29 +33,41 @@ altosui_JAVA = \ AltosFile.java \ AltosFlash.java \ AltosFlashUI.java \ + AltosFlightDisplay.java \ AltosFlightInfoTableModel.java \ - AltosFlightStatusTableModel.java \ + AltosFlightReader.java \ + AltosFlightStatus.java \ + AltosFlightUI.java \ AltosGPS.java \ AltosGreatCircle.java \ AltosHexfile.java \ Altos.java \ + AltosIgnite.java \ + AltosIgniteUI.java \ AltosInfoTable.java \ AltosKML.java \ + AltosLanded.java \ + AltosLed.java \ + AltosLights.java \ AltosLine.java \ - AltosLogfileChooser.java \ AltosLog.java \ + AltosPad.java \ AltosParse.java \ AltosPreferences.java \ AltosReader.java \ AltosRecord.java \ AltosRecordIterable.java \ - AltosReplayThread.java \ + AltosTelemetryReader.java \ + AltosReplayReader.java \ AltosRomconfig.java \ AltosRomconfigUI.java \ AltosSerial.java \ + AltosSerialInUseException.java \ AltosSerialMonitor.java \ + AltosSiteMap.java \ + AltosSiteMapCache.java \ + AltosSiteMapTile.java \ AltosState.java \ - AltosStatusTable.java \ AltosTelemetry.java \ AltosTelemetryIterable.java \ AltosUI.java \ @@ -60,7 +77,7 @@ altosui_JAVA = \ AltosGraph.java \ AltosGraphTime.java \ AltosGraphUI.java \ - AltosGraphDataChooser.java \ + AltosDataChooser.java \ AltosVoice.java JFREECHART_CLASS= \ @@ -88,8 +105,21 @@ JAR=altosui.jar FATJAR=altosui-fat.jar # Icons -JAVA_ICON=$(top_srcdir)/icon/altus-metrum-16x16.jpg -WINDOWS_ICON=$(top_srcdir)/icon/altus-metrum.ico +ICONDIR=$(top_srcdir)/icon + +JAVA_ICON=$(ICONDIR)/altus-metrum-16x16.jpg + +ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \ + $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \ + $(ICONDIR)/grayled.png $(ICONDIR)/grayoff.png + +# icon base names for jar +ICONJAR= -C $(ICONDIR) altus-metrum-16x16.jpg \ + -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \ + -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \ + -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png + +WINDOWS_ICON=$(ICONDIR)/altus-metrum.ico # Firmware FIRMWARE_TD=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx @@ -159,15 +189,15 @@ classes/altosui: $(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) jar cfm $@ Manifest.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ - -C ../libaltos libaltosJNI + -C libaltos libaltosJNI $(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON) jar cfm $@ Manifest-fat.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ - -C ../libaltos libaltosJNI + -C libaltos libaltosJNI Manifest.txt: Makefile echo 'Main-Class: altosui.AltosUI' > $@ @@ -184,25 +214,25 @@ altosui: Makefile altosui-test: Makefile echo "#!/bin/sh" > $@ - echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="../libaltos/.libs" -jar altosui.jar "$$@"' >> $@ + echo 'exec java -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="libaltos/.libs" -jar altosui.jar "$$@"' >> $@ chmod +x $@ altosui-jdb: Makefile echo "#!/bin/sh" > $@ - echo 'exec jdb -classpath "classes:../libaltos:$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="../libaltos/.libs" altosui/AltosUI "$$@"' >> $@ + echo 'exec jdb -classpath "classes:libaltos:$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="libaltos/.libs" altosui/AltosUI "$$@"' >> $@ chmod +x $@ libaltos.so: -rm -f "$@" - $(LN_S) ../libaltos/.libs/"$@" . + $(LN_S) libaltos/.libs/"$@" . libaltos.dylib: -rm -f "$@" - $(LN_S) ../libaltos/"$@" . + $(LN_S) libaltos/"$@" . altos.dll: -rm -f "$@" - $(LN_S) ../libaltos/"$@" . + $(LN_S) libaltos/"$@" . $(FREETTS_CLASS): -rm -f "$@" diff --git a/ao-tools/altosui/altos-windows.nsi b/altosui/altos-windows.nsi index 37777fd6..37777fd6 100644 --- a/ao-tools/altosui/altos-windows.nsi +++ b/altosui/altos-windows.nsi diff --git a/ao-tools/altosui/altosui-fat b/altosui/altosui-fat index 95b1c051..95b1c051 100755 --- a/ao-tools/altosui/altosui-fat +++ b/altosui/altosui-fat diff --git a/ao-tools/altosui/altosui.1 b/altosui/altosui.1 index 57fa4489..57fa4489 100644 --- a/ao-tools/altosui/altosui.1 +++ b/altosui/altosui.1 diff --git a/ao-tools/altosui/altusmetrum.jpg b/altosui/altusmetrum.jpg Binary files differindex 04027921..04027921 100644 --- a/ao-tools/altosui/altusmetrum.jpg +++ b/altosui/altusmetrum.jpg diff --git a/ao-tools/libaltos/.gitignore b/altosui/libaltos/.gitignore index c490e6f8..c490e6f8 100644 --- a/ao-tools/libaltos/.gitignore +++ b/altosui/libaltos/.gitignore diff --git a/ao-tools/libaltos/Makefile-standalone b/altosui/libaltos/Makefile-standalone index 4e438050..4e438050 100644 --- a/ao-tools/libaltos/Makefile-standalone +++ b/altosui/libaltos/Makefile-standalone diff --git a/ao-tools/libaltos/Makefile.am b/altosui/libaltos/Makefile.am index 388d2104..388d2104 100644 --- a/ao-tools/libaltos/Makefile.am +++ b/altosui/libaltos/Makefile.am diff --git a/ao-tools/libaltos/altos.dll b/altosui/libaltos/altos.dll Binary files differindex 28e9b4ad..28e9b4ad 100755 --- a/ao-tools/libaltos/altos.dll +++ b/altosui/libaltos/altos.dll diff --git a/ao-tools/libaltos/cjnitest.c b/altosui/libaltos/cjnitest.c index c6d6e069..c6d6e069 100644 --- a/ao-tools/libaltos/cjnitest.c +++ b/altosui/libaltos/cjnitest.c diff --git a/ao-tools/libaltos/libaltos.c b/altosui/libaltos/libaltos.c index 465f0ac8..465f0ac8 100644 --- a/ao-tools/libaltos/libaltos.c +++ b/altosui/libaltos/libaltos.c diff --git a/ao-tools/libaltos/libaltos.dylib b/altosui/libaltos/libaltos.dylib Binary files differindex 89aa12e7..89aa12e7 100755 --- a/ao-tools/libaltos/libaltos.dylib +++ b/altosui/libaltos/libaltos.dylib diff --git a/ao-tools/libaltos/libaltos.h b/altosui/libaltos/libaltos.h index 6e94899e..6e94899e 100644 --- a/ao-tools/libaltos/libaltos.h +++ b/altosui/libaltos/libaltos.h diff --git a/ao-tools/libaltos/libaltos.i0 b/altosui/libaltos/libaltos.i0 index d06468f5..d06468f5 100644 --- a/ao-tools/libaltos/libaltos.i0 +++ b/altosui/libaltos/libaltos.i0 diff --git a/ao-bringup/turnon_teledongle b/ao-bringup/turnon_teledongle index 216afa2a..5145e9b0 100755 --- a/ao-bringup/turnon_teledongle +++ b/ao-bringup/turnon_teledongle @@ -42,7 +42,7 @@ read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD --cal $CAL_VALUE /usr/share/altos/teledongle-v0.2*.ihx $SERIAL +$AOLOAD --cal $CAL_VALUE /usr/share/altos/stable/teledongle-v0.2*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo "Unplug and replug USB, cu to the board, confirm freq and record power" diff --git a/ao-bringup/turnon_telemetrum b/ao-bringup/turnon_telemetrum index 440eda1b..405247fa 100755 --- a/ao-bringup/turnon_telemetrum +++ b/ao-bringup/turnon_telemetrum @@ -42,7 +42,7 @@ read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD --cal $CAL_VALUE /usr/share/altos/telemetrum-v1.0*.ihx $SERIAL +$AOLOAD --cal $CAL_VALUE /usr/share/altos/stable/telemetrum-v1.0*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo "Unplug and replug USB, cu to the board, confirm freq and record power" diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 54dc777a..2850e909 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -1 +1 @@ -SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view libaltos altosui +SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view diff --git a/ao-tools/altosui/AltosGraphDataChooser.java b/ao-tools/altosui/AltosGraphDataChooser.java deleted file mode 100644 index caa14118..00000000 --- a/ao-tools/altosui/AltosGraphDataChooser.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright © 2010 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package altosui; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; - -import altosui.AltosPreferences; -import altosui.AltosDataPointReader; -import altosui.AltosEepromIterable; -import altosui.AltosTelemetryIterable; - -public class AltosGraphDataChooser extends JFileChooser { - JFrame frame; - String filename; - File file; - - public String filename() { - return filename; - } - - public File file() { - return file; - } - - public Iterable<AltosDataPoint> runDialog() { - int ret; - - ret = showOpenDialog(frame); - if (ret == APPROVE_OPTION) { - file = getSelectedFile(); - if (file == null) - return null; - filename = file.getName(); - try { - if (filename.endsWith("eeprom")) { - FileInputStream in = new FileInputStream(file); - return new AltosDataPointReader(new AltosEepromIterable(in)); - } else if (filename.endsWith("telem")) { - FileInputStream in = new FileInputStream(file); - return new AltosDataPointReader(new AltosTelemetryIterable(in)); - } else { - throw new FileNotFoundException(); - } - } catch (FileNotFoundException fe) { - JOptionPane.showMessageDialog(frame, - filename, - "Cannot open file", - JOptionPane.ERROR_MESSAGE); - } - } - return null; - } - - public AltosGraphDataChooser(JFrame in_frame) { - frame = in_frame; - setDialogTitle("Select Flight Record File"); - setFileFilter(new FileNameExtensionFilter("Flight data file", - "telem", "eeprom")); - setCurrentDirectory(AltosPreferences.logdir()); - } -} diff --git a/ao-tools/altosui/AltosStatusTable.java b/ao-tools/altosui/AltosStatusTable.java deleted file mode 100644 index 3965a57d..00000000 --- a/ao-tools/altosui/AltosStatusTable.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2010 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package altosui; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -import altosui.AltosFlightStatusTableModel; -import altosui.AltosFlightInfoTableModel; - -public class AltosStatusTable extends JTable { - private AltosFlightStatusTableModel flightStatusModel; - - JFrame frame; - - private Font statusFont = new Font("SansSerif", Font.BOLD, 24); - - public AltosStatusTable(JFrame in_frame) { - super((TableModel) new AltosFlightStatusTableModel()); - flightStatusModel = (AltosFlightStatusTableModel) getModel(); - frame = in_frame; - - setFont(statusFont); - - TableColumnModel tcm = getColumnModel(); - - for (int i = 0; i < flightStatusModel.getColumnCount(); i++) { - DefaultTableCellRenderer r = new DefaultTableCellRenderer(); - r.setFont(statusFont); - r.setHorizontalAlignment(SwingConstants.CENTER); - tcm.getColumn(i).setCellRenderer(r); - } - - setRowHeight(rowHeight()); - setShowGrid(false); - } - - public int rowHeight() { - FontMetrics statusMetrics = getFontMetrics(statusFont); - return (statusMetrics.getHeight() + statusMetrics.getLeading()) * 15 / 10; - } - - public int height() { - return rowHeight * 4; - } - - public void set(AltosState state) { - flightStatusModel.set(state); - } -} diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java deleted file mode 100644 index 28ed42fb..00000000 --- a/ao-tools/altosui/AltosUI.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright © 2010 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package altosui; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; - -import altosui.Altos; -import altosui.AltosSerial; -import altosui.AltosSerialMonitor; -import altosui.AltosRecord; -import altosui.AltosTelemetry; -import altosui.AltosState; -import altosui.AltosDeviceDialog; -import altosui.AltosPreferences; -import altosui.AltosLog; -import altosui.AltosVoice; -import altosui.AltosFlightInfoTableModel; -import altosui.AltosChannelMenu; -import altosui.AltosFlashUI; -import altosui.AltosLogfileChooser; -import altosui.AltosCSVUI; -import altosui.AltosLine; -import altosui.AltosStatusTable; -import altosui.AltosInfoTable; -import altosui.AltosDisplayThread; - -import libaltosJNI.*; - -public class AltosUI extends JFrame { - private int channel = -1; - - private AltosStatusTable flightStatus; - private AltosInfoTable flightInfo; - private AltosSerial serial_line; - private AltosLog altos_log; - private Box vbox; - - public AltosVoice voice = new AltosVoice(); - - public static boolean load_library(Frame frame) { - if (!AltosDevice.load_library()) { - JOptionPane.showMessageDialog(frame, - String.format("No AltOS library in \"%s\"", - System.getProperty("java.library.path","<undefined>")), - "Cannot load device access library", - JOptionPane.ERROR_MESSAGE); - return false; - } - return true; - } - - public AltosUI() { - - load_library(null); - - String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" }; - Object[][] statusData = { { "0", "pad", "-50", "0" } }; - - java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); - if (imgURL != null) - setIconImage(new ImageIcon(imgURL).getImage()); - - AltosPreferences.init(this); - - vbox = Box.createVerticalBox(); - this.add(vbox); - - flightStatus = new AltosStatusTable(this); - - vbox.add(flightStatus); - - flightInfo = new AltosInfoTable(); - vbox.add(flightInfo.box()); - - setTitle("AltOS"); - - createMenu(); - - serial_line = new AltosSerial(); - altos_log = new AltosLog(serial_line); - int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); - this.setSize(new Dimension (flightInfo.width(), - flightStatus.height() + flightInfo.height())); - this.validate(); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - voice.speak("Rocket flight monitor ready."); - } - - class DeviceThread extends AltosDisplayThread { - AltosSerial serial; - LinkedBlockingQueue<AltosLine> telem; - - AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { - AltosLine l = telem.take(); - if (l.line == null) - throw new IOException("IO error"); - return new AltosTelemetry(l.line); - } - - void close(boolean interrupted) { - serial.close(); - serial.remove_monitor(telem); - } - - public DeviceThread(AltosSerial s, String in_name, AltosVoice voice, AltosStatusTable status, AltosInfoTable info) { - super(AltosUI.this, voice, status, info); - serial = s; - telem = new LinkedBlockingQueue<AltosLine>(); - serial.add_monitor(telem); - name = in_name; - } - } - - private void ConnectToDevice() { - AltosDevice device = AltosDeviceDialog.show(AltosUI.this, - AltosDevice.product_basestation); - - if (device != null) { - try { - stop_display(); - serial_line.open(device); - DeviceThread thread = new DeviceThread(serial_line, device.getPath(), voice, flightStatus, flightInfo); - serial_line.set_channel(AltosPreferences.channel()); - serial_line.set_callsign(AltosPreferences.callsign()); - run_display(thread); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - String.format("Cannot open device \"%s\"", - device.getPath()), - "Cannot open target device", - JOptionPane.ERROR_MESSAGE); - } catch (IOException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - device.getPath(), - "Unkonwn I/O error", - JOptionPane.ERROR_MESSAGE); - } - } - } - - void DisconnectFromDevice () { - stop_display(); - } - - void ConfigureCallsign() { - String result; - result = JOptionPane.showInputDialog(AltosUI.this, - "Configure Callsign", - AltosPreferences.callsign()); - if (result != null) { - AltosPreferences.set_callsign(result); - if (serial_line != null) - serial_line.set_callsign(result); - } - } - - void ConfigureTeleMetrum() { - new AltosConfig(AltosUI.this); - } - - void FlashImage() { - new AltosFlashUI(AltosUI.this); - } - - - Thread display_thread; - - private void stop_display() { - if (display_thread != null && display_thread.isAlive()) { - display_thread.interrupt(); - try { - display_thread.join(); - } catch (InterruptedException ie) {} - } - display_thread = null; - } - - private void run_display(Thread thread) { - stop_display(); - display_thread = thread; - display_thread.start(); - } - - /* - * Replay a flight from telemetry data - */ - private void Replay() { - AltosLogfileChooser chooser = new AltosLogfileChooser( - AltosUI.this); - AltosRecordIterable iterable = chooser.runDialog(); - if (iterable != null) - run_display(new AltosReplayThread(this, iterable.iterator(), - chooser.filename(), - voice, - flightStatus, - flightInfo)); - } - - /* Connect to TeleMetrum, either directly or through - * a TeleDongle over the packet link - */ - private void SaveFlightData() { - new AltosEepromDownload(AltosUI.this); - } - - /* Load a flight log file and write out a CSV file containing - * all of the data in standard units - */ - - private void ExportData() { - new AltosCSVUI(AltosUI.this); - } - - /* Load a flight log CSV file and display a pretty graph. - */ - - private void GraphData() { - new AltosGraphUI(AltosUI.this); - } - - /* Create the AltosUI menus - */ - private void createMenu() { - JMenuBar menubar = new JMenuBar(); - JMenu menu; - JMenuItem item; - JRadioButtonMenuItem radioitem; - - // File menu - { - menu = new JMenu("File"); - menu.setMnemonic(KeyEvent.VK_F); - menubar.add(menu); - - item = new JMenuItem("Replay File",KeyEvent.VK_R); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Replay(); - } - }); - menu.add(item); - - item = new JMenuItem("Save Flight Data",KeyEvent.VK_S); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - SaveFlightData(); - } - }); - menu.add(item); - - item = new JMenuItem("Flash Image",KeyEvent.VK_F); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - FlashImage(); - } - }); - menu.add(item); - - item = new JMenuItem("Export Data",KeyEvent.VK_F); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ExportData(); - } - }); - menu.add(item); - - item = new JMenuItem("Graph Data",KeyEvent.VK_F); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - GraphData(); - } - }); - menu.add(item); - - item = new JMenuItem("Quit",KeyEvent.VK_Q); - item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, - ActionEvent.CTRL_MASK)); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - System.exit(0); - } - }); - menu.add(item); - } - - // Device menu - { - menu = new JMenu("Device"); - menu.setMnemonic(KeyEvent.VK_D); - menubar.add(menu); - - item = new JMenuItem("Connect to Device",KeyEvent.VK_C); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConnectToDevice(); - } - }); - menu.add(item); - - item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - DisconnectFromDevice(); - } - }); - menu.add(item); - - menu.addSeparator(); - - item = new JMenuItem("Set Callsign",KeyEvent.VK_S); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureCallsign(); - } - }); - - menu.add(item); - - item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConfigureTeleMetrum(); - } - }); - - menu.add(item); - } - // Log menu - { - menu = new JMenu("Log"); - menu.setMnemonic(KeyEvent.VK_L); - menubar.add(menu); - - item = new JMenuItem("New Log",KeyEvent.VK_N); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - } - }); - menu.add(item); - - item = new JMenuItem("Configure Log",KeyEvent.VK_C); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - AltosPreferences.ConfigureLog(); - } - }); - menu.add(item); - } - // Voice menu - { - menu = new JMenu("Voice", true); - menu.setMnemonic(KeyEvent.VK_V); - menubar.add(menu); - - radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice()); - radioitem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource(); - boolean enabled = item.isSelected(); - AltosPreferences.set_voice(enabled); - if (enabled) - voice.speak_always("Enable voice."); - else - voice.speak_always("Disable voice."); - } - }); - menu.add(radioitem); - item = new JMenuItem("Test Voice",KeyEvent.VK_T); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - voice.speak("That's one small step for man; one giant leap for mankind."); - } - }); - menu.add(item); - } - - // Channel menu - { - menu = new AltosChannelMenu(AltosPreferences.channel()); - menu.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int new_channel = Integer.parseInt(e.getActionCommand()); - AltosPreferences.set_channel(new_channel); - serial_line.set_channel(new_channel); - } - }); - menu.setMnemonic(KeyEvent.VK_C); - menubar.add(menu); - } - - this.setJMenuBar(menubar); - - } - - static AltosRecordIterable open_logfile(String filename) { - File file = new File (filename); - try { - FileInputStream in; - - in = new FileInputStream(file); - if (filename.endsWith("eeprom")) - return new AltosEepromIterable(in); - else - return new AltosTelemetryIterable(in); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static AltosWriter open_csv(String filename) { - File file = new File (filename); - try { - return new AltosCSV(file); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static AltosWriter open_kml(String filename) { - File file = new File (filename); - try { - return new AltosKML(file); - } catch (FileNotFoundException fe) { - System.out.printf("Cannot open '%s'\n", filename); - return null; - } - } - - static final int process_csv = 1; - static final int process_kml = 2; - - static void process_file(String input, int process) { - AltosRecordIterable iterable = open_logfile(input); - if (iterable == null) - return; - if (process == 0) - process = process_csv; - if ((process & process_csv) != 0) { - String output = Altos.replace_extension(input,".csv"); - System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); - if (input.equals(output)) { - System.out.printf("Not processing '%s'\n", input); - } else { - AltosWriter writer = open_csv(output); - if (writer != null) { - writer.write(iterable); - writer.close(); - } - } - } - if ((process & process_kml) != 0) { - String output = Altos.replace_extension(input,".kml"); - System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); - if (input.equals(output)) { - System.out.printf("Not processing '%s'\n", input); - } else { - AltosWriter writer = open_kml(output); - if (writer == null) - return; - writer.write(iterable); - writer.close(); - } - } - } - - public static void main(final String[] args) { - int process = 0; - /* Handle batch-mode */ - if (args.length > 0) { - for (int i = 0; i < args.length; i++) { - if (args[i].equals("--kml")) - process |= process_kml; - else if (args[i].equals("--csv")) - process |= process_csv; - else - process_file(args[i], process); - } - } else { - AltosUI altosui = new AltosUI(); - altosui.setVisible(true); - } - } -} diff --git a/configure.ac b/configure.ac index 903c5765..22af00ab 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,8 @@ PKG_CHECK_MODULES([SNDFILE], [sndfile]) AC_OUTPUT([ Makefile +altosui/Makefile +altosui/libaltos/Makefile ao-tools/Makefile ao-tools/lib/Makefile ao-tools/ao-rawload/Makefile @@ -144,8 +146,6 @@ ao-tools/ao-list/Makefile ao-tools/ao-load/Makefile ao-tools/ao-postflight/Makefile ao-tools/ao-view/Makefile -ao-tools/libaltos/Makefile -ao-tools/altosui/Makefile ao-utils/Makefile src/Version ]) diff --git a/contrib/arch-linux/PKGBUILD-git.freetts b/contrib/arch-linux/PKGBUILD-git.freetts new file mode 100644 index 00000000..b16a994d --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.freetts @@ -0,0 +1,38 @@ +# Original contributor: Bob Finch <w9ya@qrpqrci.net> + +pkgname=freetts +_pkgname=FreeTTS +pkgver=1.2.2 +_pkgver=1.2 +pkgrel=1 +pkgdesc="Sun's rewrite of flite for java" +arch=('any') +license=('custom') +depends=('java-environment') +makedepends=('junit' 'apache-ant') +#source=(http://downloads.sourceforge.net/project/\ +#$pkgname/$_pkgname/$_pkgname%20$pkgver/$pkgname-$pkgver-src.zip) +source=(http://downloads.sourceforge.net/project/\ +$pkgname/$_pkgname/$_pkgname%20$pkgver/$pkgname-$pkgver-bin.zip) +url="http://freetts.sourceforge.net/" +#md5sums=('692b5ece251fed88539736e55af5f391') +md5sums=('cd751e5fd5c7ed29cf6879fc5200605d') + +build() { +# [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh +# [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + +# cd ${startdir}/src/$pkgname-$pkgver/lib + cd ${startdir}/src/$pkgname-$_pkgver/lib + rm README.txt jsapi.sh jsapi.exe + +# cd ${startdir}/src/$pkgname-$pkgver +# ln -s . src +# /usr/share/java/apache-ant/bin/ant + + cd $srcdir/$pkgname-$_pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 lib/* $pkgdir/usr/share/java/$pkgname/lib/ + +# cp -a bld $pkgdir/usr/share/java/$pkgname/ +} diff --git a/contrib/arch-linux/PKGBUILD-git.jcommon b/contrib/arch-linux/PKGBUILD-git.jcommon new file mode 100644 index 00000000..8c435436 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.jcommon @@ -0,0 +1,28 @@ +# Original contributor: Bob Finch <w9ya@qrpqrci.net> + +pkgname=jcommon +_pkgname=JCommon +_project=jfreechart +pkgver=1.0.16 +pkgrel=1 +pkgdesc="Base routines for JFreeChart" +arch=('any') +license=('lgpl') +depends=('java-environment') +makedepends=('apache-ant' 'zip' 'gzip' 'tar') +source=(http://downloads.sourceforge.net/project/\ +$_project/3.%20$_pkgname/$pkgver/$pkgname-$pkgver.tar.gz) +url="http://www.jfree.org/jcommon/" +md5sums=('5fb774c225cdc7d15a99c9702031ae05') + +build() { + [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh + [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + + cd ${startdir}/src/$pkgname-$pkgver/ant + /usr/share/java/apache-ant/bin/ant + + cd ${startdir}/src/$pkgname-$pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 $pkgname-$pkgver.jar $pkgdir/usr/share/java/$pkgname/lib/$pkgname.jar +} diff --git a/contrib/arch-linux/PKGBUILD-git.jfreechart b/contrib/arch-linux/PKGBUILD-git.jfreechart new file mode 100644 index 00000000..f7427983 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.jfreechart @@ -0,0 +1,27 @@ +# Original contributor: Bob Finch <w9ya@qrpqrci.net> + +pkgname=jfreechart +_pkgname=JFreeChart +pkgver=1.0.13 +pkgrel=1 +pkgdesc="Charting program for Java" +arch=('any') +license=('lgpl') +depends=('java-environment' 'jcommon') +makedepends=('apache-ant' 'zip' 'gzip' 'tar') +source=(http://downloads.sourceforge.net/project/\ +$pkgname/1.%20$_pkgname/$pkgver/$pkgname-$pkgver.tar.gz) +url="http://www.jfree.org/jfreechart/" +md5sums=('c90e2f8f612b9aaf3f24a4afce219076') + +build() { + [ -z "${JAVA_HOME}" ] && . /etc/profile.d/jdk.sh + [ -z "${ANT_HOME}" ] && . /etc/profile.d/apache-ant.sh + + cd ${startdir}/src/$pkgname-$pkgver/ant + /usr/share/java/apache-ant/bin/ant + + cd ${startdir}/src/$pkgname-$pkgver + install -d $pkgdir/usr/share/java/$pkgname/lib + install -m644 lib/$pkgname-$pkgver.jar $pkgdir/usr/share/java/$pkgname/lib/$pkgname.jar +} diff --git a/contrib/arch-linux/PKGBUILD-git.nsis.patched b/contrib/arch-linux/PKGBUILD-git.nsis.patched new file mode 100644 index 00000000..fe751cb9 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.nsis.patched @@ -0,0 +1,34 @@ +# Contributor: Andre Klitzing <andre () incubo () de> +# Contributor: mosra <mosra@centrum.cz> +pkgname=nsis +pkgver=2.46 +pkgrel=3 +pkgdesc='A professional open source system to create Windows installers' +arch=('i686' 'x86_64') +url='http://nsis.sourceforge.net' +license='http://nsis.sourceforge.net/License' +depends=('mingw32-runtime') +makedepends=('scons' 'mingw32-gcc' 'mingw32-binutils' 'mingw32-w32api') +source=(http://downloads.sourceforge.net/project/nsis/NSIS%202/$pkgver/$pkgname-$pkgver-src.tar.bz2 + nsis-2.43-64bit-fixes.patch) +md5sums=('61c2e81739436b06d7cf7bcce1d533ac' + '9eead3b78da54e3afda8f6a5b663aea9') + +build() { + cd "$srcdir/$pkgname-$pkgver-src" + + # Patch taken from + # http://cvs.fedoraproject.org/viewvc/rpms/mingw32-nsis/F-11/nsis-2.43-64bit-fixes.patch + patch -p1 -i "$srcdir/nsis-2.43-64bit-fixes.patch" || return 1 + + # Patch version from DD-MM-YYY.cvs to 2.46 (makes CPack working again) + sed -i "s/'Version of NSIS', cvs_version)/'Version of NSIS', '${pkgver}')/" \ + "${srcdir}/${pkgname}-${pkgver}-src/SConstruct" + + scons PREFIX_DEST="$pkgdir/" PREFIX=/usr/i486-mingw32 SKIPUTILS='NSIS Menu' install || return 1 + + # Add a symlink to 'makensis' for lazy people ;-) + mkdir "$pkgdir/usr/bin/" + cd "$pkgdir/usr/bin/" + ln -s ../i486-mingw32/bin/makensis +} diff --git a/contrib/arch-linux/PKGBUILD-git.sdcc_patched b/contrib/arch-linux/PKGBUILD-git.sdcc_patched new file mode 100644 index 00000000..1a78c292 --- /dev/null +++ b/contrib/arch-linux/PKGBUILD-git.sdcc_patched @@ -0,0 +1,34 @@ +# $Id: PKGBUILD 23526 2010-08-12 12:59:41Z spupykin $ +# Maintainer: Sergej Pupykin <pupykin.s+arch@gmail.com> +# Maintainer: Jose Negron <josenj.arch@mailnull.net> +# Patched w/ keith packard's patch for altos - RJF 26-aug-10 + +pkgname=sdcc +pkgver=2.9.0 +pkgrel=2_patched +pkgdesc="Retargettable ANSI C compiler (Intel 8051, Maxim 80DS390, Zilog Z80 and the Motorola 68HC08)" +arch=('i686' 'x86_64') +license=('GPL') +depends=('bash' 'gcc-libs') +makedepends=('gputils' 'flex' 'bison' 'patch') +provides=('sdcc') +conflicts=('sdcc') +url="http://sdcc.sourceforge.net/" +options=(!strip) +#Patch file was taken from https://bugzilla.redhat.com/show_bug.cgi?id=488217 +source=(http://downloads.sourceforge.net/sourceforge/sdcc/$pkgname-src-$pkgver.tar.bz2 + http://aur.archlinux.org/packages/$pkgname/$pkgname/$pkgname-$pkgver.patch + new.patch) +md5sums=('a6151ed328fd3bc48305ffbc628dc122' + '35313a8edca4f2c8a03ad57036da4e62' + '65612bb094e719713bc477efd6000672') + +build() { + cd $srcdir/$pkgname + patch -p1 -i ../$pkgname-$pkgver.patch + patch -p0 -i ../new.patch + ./configure --prefix=$pkgdir/usr + make + make install + strip $pkgdir/usr/bin/* || true +} diff --git a/contrib/arch-linux/new.patch b/contrib/arch-linux/new.patch new file mode 100644 index 00000000..74e1df06 --- /dev/null +++ b/contrib/arch-linux/new.patch @@ -0,0 +1,35 @@ +--- src/SDCCast.c ++++ src/SDCCast.c +@@ -863,6 +863,8 @@ processParms (ast *func, + + ftype = (*actParm)->ftype; + ++ resultType = RESULT_TYPE_NONE; ++ + /* If it's a char, upcast to int. */ + if (IS_INTEGRAL (ftype) + && (getSize (ftype) < (unsigned) INTSIZE)) +@@ -874,12 +876,14 @@ processParms (ast *func, + { + newType = newAst_LINK (copyLinkChain(ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; ++ resultType = RESULT_TYPE_GPTR; + } + + if (IS_AGGREGATE (ftype)) + { + newType = newAst_LINK (copyLinkChain (ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; ++ resultType = RESULT_TYPE_GPTR; + } + + if (newType) +@@ -890,7 +894,7 @@ processParms (ast *func, + (*actParm)->filename = (*actParm)->right->filename; + (*actParm)->lineno = (*actParm)->right->lineno; + +- decorateType (*actParm, RESULT_TYPE_NONE); ++ decorateType (*actParm, resultType); + } + return 0; + } /* vararg */ diff --git a/contrib/arch-linux/nsis-2.43-64bit-fixes.patch b/contrib/arch-linux/nsis-2.43-64bit-fixes.patch new file mode 100644 index 00000000..342396d7 --- /dev/null +++ b/contrib/arch-linux/nsis-2.43-64bit-fixes.patch @@ -0,0 +1,211 @@ +diff -ur nsis-2.43-src/SCons/Config/gnu nsis-2.43-src-64bit-fixes/SCons/Config/gnu +--- nsis-2.43-src/SCons/Config/gnu 2009-02-05 01:52:28.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/SCons/Config/gnu 2009-02-25 07:59:44.000000000 +0100 +@@ -95,8 +95,6 @@ + makensis_env.Append(CXXFLAGS = ['-Wall']) # all warnings + + conf = FlagsConfigure(makensis_env) +-conf.CheckCompileFlag('-m32') # +-conf.CheckLinkFlag('-m32') # + conf.CheckLinkFlag('$MAP_FLAG') # generate map file + if not defenv['DEBUG'] and defenv['STRIP'] and defenv['STRIP_CP']: + TestStrip(conf) # strip +@@ -149,8 +147,6 @@ + ### cross-platform util environment adjustments + + conf = FlagsConfigure(cp_util_env) +-conf.CheckCompileFlag('-m32') +-conf.CheckLinkFlag('-m32') + if not defenv['DEBUG'] and defenv['STRIP'] and defenv['STRIP_CP']: + TestStrip(conf) # strip + conf.Finish() +@@ -160,8 +156,6 @@ + test_env = defenv.Clone() + test_env.Append(CPPPATH = ['#$BUILD_CONFIG']) + conf = FlagsConfigure(test_env) +-conf.CheckCompileFlag('-m32') +-conf.CheckLinkFlag('-m32') + conf.Finish() + + ### weird GCC requirements +diff -ur nsis-2.43-src/Source/DialogTemplate.cpp nsis-2.43-src-64bit-fixes/Source/DialogTemplate.cpp +--- nsis-2.43-src/Source/DialogTemplate.cpp 2007-11-30 10:54:13.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/DialogTemplate.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -74,7 +74,7 @@ + if (IS_INTRESOURCE(x)) { \ + *(WORD*)seeker = 0xFFFF; \ + seeker += sizeof(WORD); \ +- *(WORD*)seeker = ConvertEndianness(WORD(DWORD(x))); \ ++ *(WORD*)seeker = ConvertEndianness(WORD(long(x))); \ + seeker += sizeof(WORD); \ + } \ + else { \ +@@ -622,7 +622,7 @@ + } + } + +- assert((DWORD) seeker - (DWORD) pbDlg == dwSize); ++ assert((long) seeker - (long) pbDlg == dwSize); + + // DONE! + return pbDlg; +diff -ur nsis-2.43-src/Source/mmap.cpp nsis-2.43-src-64bit-fixes/Source/mmap.cpp +--- nsis-2.43-src/Source/mmap.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/mmap.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -322,7 +322,7 @@ + if (!pView) + return; + +- unsigned int alignment = ((unsigned int)pView) % m_iAllocationGranularity; ++ unsigned int alignment = ((unsigned long)pView) % m_iAllocationGranularity; + pView = (char *)pView - alignment; + size += alignment; + #ifdef _WIN32 +diff -ur nsis-2.43-src/Source/Platform.h nsis-2.43-src-64bit-fixes/Source/Platform.h +--- nsis-2.43-src/Source/Platform.h 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/Platform.h 2009-02-25 07:59:44.000000000 +0100 +@@ -166,7 +166,7 @@ + # define MAKEINTRESOURCE MAKEINTRESOURCEA + # endif + # ifndef IMAGE_FIRST_SECTION +-# define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (DWORD) h + \ ++# define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (long) h + \ + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + \ + FIX_ENDIAN_INT16(PIMAGE_NT_HEADERS(h)->FileHeader.SizeOfOptionalHeader) ) ) + # endif +@@ -198,7 +198,7 @@ + #endif + + #ifndef ULONG_PTR +-# define ULONG_PTR DWORD ++# define ULONG_PTR ULONG + #endif + + #ifndef IDC_HAND +@@ -703,7 +703,7 @@ + WORD e_oemid; + WORD e_oeminfo; + WORD e_res2[10]; +- LONG e_lfanew; ++ DWORD e_lfanew; + } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; + # pragma pack() + # pragma pack(4) +diff -ur nsis-2.43-src/Source/Plugins.cpp nsis-2.43-src-64bit-fixes/Source/Plugins.cpp +--- nsis-2.43-src/Source/Plugins.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/Plugins.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -136,7 +136,7 @@ + DWORD prd = FIX_ENDIAN_INT32(sections[i].PointerToRawData); + PIMAGE_EXPORT_DIRECTORY exports = PIMAGE_EXPORT_DIRECTORY(&dlldata[0] + prd + ExportDirVA - va); + DWORD na = FIX_ENDIAN_INT32(exports->AddressOfNames); +- unsigned long *names = (unsigned long*)((unsigned long) exports + (char *) na - ExportDirVA); ++ unsigned int *names = (unsigned int*)((unsigned long) exports + (char *) na - ExportDirVA); + for (unsigned long j = 0; j < FIX_ENDIAN_INT32(exports->NumberOfNames); j++) + { + const string name = string((char*)exports + FIX_ENDIAN_INT32(names[j]) - ExportDirVA); +diff -ur nsis-2.43-src/Source/ResourceEditor.cpp nsis-2.43-src-64bit-fixes/Source/ResourceEditor.cpp +--- nsis-2.43-src/Source/ResourceEditor.cpp 2009-02-05 01:50:12.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/ResourceEditor.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -684,7 +684,7 @@ + rdDir.NumberOfIdEntries = ConvertEndianness(rdDir.NumberOfIdEntries); + + CopyMemory(seeker, &rdDir, sizeof(IMAGE_RESOURCE_DIRECTORY)); +- crd->m_dwWrittenAt = DWORD(seeker); ++ crd->m_dwWrittenAt = long(seeker); + seeker += sizeof(IMAGE_RESOURCE_DIRECTORY); + + for (int i = 0; i < crd->CountEntries(); i++) { +@@ -705,7 +705,7 @@ + rDirE.UName.NameString.NameIsString = (crd->GetEntry(i)->HasName()) ? 1 : 0; + + CopyMemory(seeker, &rDirE, sizeof(MY_IMAGE_RESOURCE_DIRECTORY_ENTRY)); +- crd->GetEntry(i)->m_dwWrittenAt = DWORD(seeker); ++ crd->GetEntry(i)->m_dwWrittenAt = long(seeker); + seeker += sizeof(MY_IMAGE_RESOURCE_DIRECTORY_ENTRY); + } + qDirs.pop(); +@@ -721,7 +721,7 @@ + rDataE.Size = ConvertEndianness(cRDataE->GetSize()); + + CopyMemory(seeker, &rDataE, sizeof(IMAGE_RESOURCE_DATA_ENTRY)); +- cRDataE->m_dwWrittenAt = DWORD(seeker); ++ cRDataE->m_dwWrittenAt = long(seeker); + seeker += sizeof(IMAGE_RESOURCE_DATA_ENTRY); + + qDataEntries.pop(); +@@ -733,7 +733,7 @@ + while (!qStrings.empty()) { + CResourceDirectoryEntry* cRDirE = qStrings.front(); + +- PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_dwWrittenAt)->UName.NameString.NameOffset = ConvertEndianness(DWORD(seeker) - DWORD(pbRsrcSec)); ++ PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_dwWrittenAt)->UName.NameString.NameOffset = ConvertEndianness(long(seeker) - long(pbRsrcSec)); + + WCHAR* szName = cRDirE->GetName(); + WORD iLen = winchar_strlen(szName) + 1; +@@ -764,7 +764,7 @@ + /* + * Set all of the directory entries offsets. + */ +- SetOffsets(m_cResDir, DWORD(pbRsrcSec)); ++ SetOffsets(m_cResDir, long(pbRsrcSec)); + } + + // Sets the offsets in directory entries +@@ -887,7 +887,7 @@ + // Returns -1 if can not be found + int CResourceDirectory::Find(WCHAR* szName) { + if (IS_INTRESOURCE(szName)) +- return Find((WORD) (DWORD) szName); ++ return Find((WORD) (long) szName); + else + if (szName[0] == '#') + return Find(WORD(winchar_stoi(szName + 1))); +@@ -965,7 +965,7 @@ + if (IS_INTRESOURCE(szName)) { + m_bHasName = false; + m_szName = 0; +- m_wId = (WORD) (DWORD) szName; ++ m_wId = (WORD) (long) szName; + } + else { + m_bHasName = true; +@@ -979,7 +979,7 @@ + if (IS_INTRESOURCE(szName)) { + m_bHasName = false; + m_szName = 0; +- m_wId = (WORD) (DWORD) szName; ++ m_wId = (WORD) (long) szName; + } + else { + m_bHasName = true; +diff -ur nsis-2.43-src/Source/util.cpp nsis-2.43-src-64bit-fixes/Source/util.cpp +--- nsis-2.43-src/Source/util.cpp 2009-02-01 15:44:30.000000000 +0100 ++++ nsis-2.43-src-64bit-fixes/Source/util.cpp 2009-02-25 07:59:44.000000000 +0100 +@@ -77,9 +77,9 @@ + } + + if (width != 0) { +- LONG biWidth; ++ DWORD biWidth; + fseek(f, 18, SEEK_SET); // Seek to the width member of the header +- fread(&biWidth, sizeof(LONG), 1, f); ++ fread(&biWidth, sizeof(DWORD), 1, f); + FIX_ENDIAN_INT32_INPLACE(biWidth); + if (width != biWidth) { + fclose(f); +@@ -88,12 +88,12 @@ + } + + if (height != 0) { +- LONG biHeight; ++ DWORD biHeight; + fseek(f, 22, SEEK_SET); // Seek to the height member of the header +- fread(&biHeight, sizeof(LONG), 1, f); ++ fread(&biHeight, sizeof(DWORD), 1, f); + FIX_ENDIAN_INT32_INPLACE(biHeight); + // Bitmap height can be negative too... +- if (height != abs(biHeight)) { ++ if (height != abs((long int)biHeight)) { + fclose(f); + return -3; + } diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..54ca39bc --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,3 @@ +*.html +*.pdf +*.fo diff --git a/doc/Makefile b/doc/Makefile index 238cefb0..65917ea2 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,32 +2,35 @@ # http://docbook.sourceforge.net/release/xsl/current/README # -all: telemetrum-doc.html telemetrum-doc.pdf +HTML=telemetrum-doc.html altos.html +PDF=telemetrum-doc.pdf altos.pdf +DOC=$(HTML) $(PDF) +HTMLSTYLE=/usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl +FOSTYLE=/usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl +PDFSTYLE= -publish: all - cp telemetrum-doc.html \ - telemetrum-doc.pdf /home/bdale/web/altusmetrum/TeleMetrum/doc/ - (cd /home/bdale/web/altusmetrum ; echo "update docs" | git commit -F - /home/bdale/web/altusmetrum/TeleMetrum/doc/* ; git push) +.SUFFIXES: .xsl .html .fo .pdf + +.xsl.html: + xsltproc -o $@ $(HTMLSTYLE) $*.xsl +.xsl.fo: + xsltproc -o $@ $(FOSTYLE) $*.xsl -telemetrum-doc.html: telemetrum-doc.xsl - xsltproc -o telemetrum-doc.html \ - /usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl \ - telemetrum-doc.xsl +.fo.pdf: + fop -fo $*.fo -pdf $@ -telemetrum-doc.fo: telemetrum-doc.xsl - xsltproc -o telemetrum-doc.fo \ - /usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl \ - telemetrum-doc.xsl +all: $(HTML) $(PDF) -telemetrum-doc.pdf: telemetrum-doc.fo - fop -fo telemetrum-doc.fo -pdf telemetrum-doc.pdf +publish: $(DOC) + cp $(DOC)telemetrum-doc.html home/bdale/web/altusmetrum/TeleMetrum/doc/ + (cd /home/bdale/web/altusmetrum ; echo "update docs" | git commit -F - /home/bdale/web/altusmetrum/TeleMetrum/doc/* ; git push) clean: - rm -f telemetrum-doc.html telemetrum-doc.pdf telemetrum-doc.fo + rm -f *.html *.pdf *.fo distclean: - rm -f telemetrum-doc.html telemetrum-doc.pdf telemetrum-doc.fo + rm -f *.html *.pdf *.fo indent: telemetrum-doc.xsl xmlindent -i 2 < telemetrum-doc.xsl > telemetrum-doc.new diff --git a/doc/altos.xsl b/doc/altos.xsl new file mode 100644 index 00000000..9a88a5b5 --- /dev/null +++ b/doc/altos.xsl @@ -0,0 +1,1441 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" + "/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd"> + +<book> + <title>AltOS</title> + <subtitle>Altos Metrum Operating System</subtitle> + <bookinfo> + <author> + <firstname>Keith</firstname> + <surname>Packard</surname> + </author> + <copyright> + <year>2010</year> + <holder>Keith Packard</holder> + </copyright> + <legalnotice> + <para> + This document is released under the terms of the + <ulink url="http://creativecommons.org/licenses/by-sa/3.0/"> + Creative Commons ShareAlike 3.0 + </ulink> + license. + </para> + </legalnotice> + <revhistory> + <revision> + <revnumber>0.1</revnumber> + <date>22 November 2010</date> + <revremark>Initial content</revremark> + </revision> + </revhistory> + </bookinfo> + <chapter> + <title>Overview</title> + <para> + AltOS is a operating system built for the 8051-compatible + processor found in the TI cc1111 microcontroller. It's designed + to be small and easy to program with. The main features are: + <itemizedlist> + <listitem> + <para>Multi-tasking. While the 8051 doesn't provide separate + address spaces, it's often easier to write code that operates + in separate threads instead of tying everything into one giant + event loop. + </para> + </listitem> + <listitem> + <para>Non-preemptive. This increases latency for thread + switching but reduces the number of places where context + switching can occur. It also simplifies the operating system + design somewhat. Nothing in the target system (rocket flight + control) has tight timing requirements, and so this seems like + a reasonable compromise. + </para> + </listitem> + <listitem> + <para>Sleep/wakeup scheduling. Taken directly from ancient + Unix designs, these two provide the fundemental scheduling + primitive within AltOS. + </para> + </listitem> + <listitem> + <para>Mutexes. As a locking primitive, mutexes are easier to + use than semaphores, at least in my experience. + </para> + </listitem> + <listitem> + <para>Timers. Tasks can set an alarm which will abort any + pending sleep, allowing operations to time-out instead of + blocking forever. + </para> + </listitem> + </itemizedlist> + </para> + <para> + The device drivers and other subsystems in AltOS are + conventionally enabled by invoking their _init() function from + the 'main' function before that calls + ao_start_scheduler(). These functions initialize the pin + assignments, add various commands to the command processor and + may add tasks to the scheduler to handle the device. A typical + main program, thus, looks like: + <programlisting> +void +main(void) +{ + ao_clock_init(); + + /* Turn on the LED until the system is stable */ + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_RED); + ao_timer_init(); + ao_cmd_init(); + ao_usb_init(); + ao_monitor_init(AO_LED_GREEN, TRUE); + ao_rssi_init(AO_LED_RED); + ao_radio_init(); + ao_packet_slave_init(); + ao_packet_master_init(); +#if HAS_DBG + ao_dbg_init(); +#endif + ao_config_init(); + ao_start_scheduler(); +} + </programlisting> + As you can see, a long sequence of subsystems are initialized + and then the scheduler is started. + </para> + </chapter> + <chapter> + <title>Programming the 8051 with SDCC</title> + <para> + The 8051 is a primitive 8-bit processor, designed in the mists + of time in as few transistors as possible. The architecture is + highly irregular and includes several separate memory + spaces. Furthermore, accessing stack variables is slow, and the + stack itself is of limited size. While SDCC papers over the + instruction set, it is not completely able to hide the memory + architecture from the application designer. + </para> + <section> + <title>8051 memory spaces</title> + <para> + The __data/__xdata/__code memory spaces below were completely + separate in the original 8051 design. In the cc1111, this + isn't true—they all live in a single unified 64kB address + space, and so it's possible to convert any address into a + unique 16-bit address. SDCC doesn't know this, and so a + 'global' address to SDCC consumes 3 bytes of memory, 1 byte as + a tag indicating the memory space and 2 bytes of offset within + that space. AltOS avoids these 3-byte addresses as much as + possible; using them involves a function call per byte + access. The result is that nearly every variable declaration + is decorated with a memory space identifier which clutters the + code but makes the resulting code far smaller and more + efficient. + </para> + <variablelist> + <title>SDCC 8051 memory spaces</title> + <varlistentry> + <term>__data</term> + <listitem> + <para> + The 8051 can directly address these 128 bytes of + memory. This makes them precious so they should be + reserved for frequently addressed values. Oh, just to + confuse things further, the 8 general registers in the + CPU are actually stored in this memory space. There are + magic instructions to 'bank switch' among 4 banks of + these registers located at 0x00 - 0x1F. AltOS uses only + the first bank at 0x00 - 0x07, leaving the other 24 + bytes available for other data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__idata</term> + <listitem> + <para> + There are an additional 128 bytes of internal memory + that share the same address space as __data but which + cannot be directly addressed. The stack normally + occupies this space and so AltOS doesn't place any + static storage here. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__xdata</term> + <listitem> + <para> + This is additional general memory accessed through a + single 16-bit address register. The CC1111F32 has 32kB + of memory available here. Most program data should live + in this memory space. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__pdata</term> + <listitem> + <para> + This is an alias for the first 256 bytes of __xdata + memory, but uses a shorter addressing mode with + single global 8-bit value for the high 8 bits of the + address and any of several 8-bit registers for the low 8 + bits. AltOS uses a few bits of this memory, it should + probably use more. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__code</term> + <listitem> + <para> + All executable code must live in this address space, but + you can stick read-only data here too. It is addressed + using the 16-bit address register and special 'code' + access opcodes. Anything read-only should live in this space. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__bit</term> + <listitem> + <para> + The 8051 has 128 bits of bit-addressible memory that + lives in the __data segment from 0x20 through + 0x2f. Special instructions access these bits + in a single atomic operation. This isn't so much a + separate address space as a special addressing mode for + a few bytes in the __data segment. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>__sfr, __sfr16, __sfr32, __sbit</term> + <listitem> + <para> + Access to physical registers in the device use this mode + which declares the variable name, it's type and the + address it lives at. No memory is allocated for these + variables. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section> + <title>Function calls on the 8051</title> + <para> + Because stack addressing is expensive, and stack space + limited, the default function call declaration in SDCC + allocates all parameters and local variables in static global + memory. Just like fortran. This makes these functions + non-reentrant, and also consume space for parameters and + locals even when they are not running. The benefit is smaller + code and faster execution. + </para> + <section> + <title>__reentrant functions</title> + <para> + All functions which are re-entrant, either due to recursion + or due to a potential context switch while executing, should + be marked as __reentrant so that their parameters and local + variables get allocated on the stack. This ensures that + these values are not overwritten by another invocation of + the function. + </para> + <para> + Functions which use significant amounts of space for + arguments and/or local variables and which are not often + invoked can also be marked as __reentrant. The resulting + code will be larger, but the savings in memory are + frequently worthwhile. + </para> + </section> + <section> + <title>Non __reentrant functions</title> + <para> + All parameters and locals in non-reentrant functions can + have data space decoration so that they are allocated in + __xdata, __pdata or __data space as desired. This can avoid + consuming __data space for infrequently used variables in + frequently used functions. + </para> + <para> + All library functions called by SDCC, including functions + for multiplying and dividing large data types, are + non-reentrant. Because of this, interrupt handlers must not + invoke any library functions, including the multiply and + divide code. + </para> + </section> + <section> + <title>__interrupt functions</title> + <para> + Interrupt functions are declared with with an __interrupt + decoration that includes the interrupt number. SDCC saves + and restores all of the registers in these functions and + uses the 'reti' instruction at the end so that they operate + as stand-alone interrupt handlers. Interrupt functions may + call the ao_wakeup function to wake AltOS tasks. + </para> + </section> + <section> + <title>__critical functions and statements</title> + <para> + SDCC has built-in support for suspending interrupts during + critical code. Functions marked as __critical will have + interrupts suspended for the whole period of + execution. Individual statements may also be marked as + __critical which blocks interrupts during the execution of + that statement. Keeping critical sections as short as + possible is key to ensuring that interrupts are handled as + quickly as possible. + </para> + </section> + </section> + </chapter> + <chapter> + <title>Task functions</title> + <para> + This chapter documents how to create, destroy and schedule AltOS tasks. + </para> + <variablelist> + <title>AltOS Task Functions</title> + <varlistentry> + <term>ao_add_task</term> + <listitem> + <programlisting> +void +ao_add_task(__xdata struct ao_task * task, + void (*start)(void), + __code char *name); + </programlisting> + <para> + This initializes the statically allocated task structure, + assigns a name to it (not used for anything but the task + display), and the start address. It does not switch to the + new task. 'start' must not ever return; there is no place + to return to. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_exit</term> + <listitem> + <programlisting> +void +ao_exit(void) + </programlisting> + <para> + This terminates the current task. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_sleep</term> + <listitem> + <programlisting> +void +ao_sleep(__xdata void *wchan) + </programlisting> + <para> + This suspends the current task until 'wchan' is signaled + by ao_wakeup, or until the timeout, set by ao_alarm, + fires. If 'wchan' is signaled, ao_sleep returns 0, otherwise + it returns 1. This is the only way to switch to another task. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_wakeup</term> + <listitem> + <programlisting> +void +ao_wakeup(__xdata void *wchan) + </programlisting> + <para> + Wake all tasks blocked on 'wchan'. This makes them + available to be run again, but does not actually switch + to another task. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_alarm</term> + <listitem> + <programlisting> +void +ao_alarm(uint16_t delay) + </programlisting> + <para> + Schedules an alarm to fire in at least 'delay' ticks. If + the task is asleep when the alarm fires, it will wakeup + and ao_sleep will return 1. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_wake_task</term> + <listitem> + <programlisting> +void +ao_wake_task(__xdata struct ao_task *task) + </programlisting> + <para> + Force a specific task to wake up, independent of which + 'wchan' it is waiting for. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_start_scheduler</term> + <listitem> + <programlisting> +void +ao_start_scheduler(void) + </programlisting> + <para> + This is called from 'main' when the system is all + initialized and ready to run. It will not return. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_clock_init</term> + <listitem> + <programlisting> +void +ao_clock_init(void) + </programlisting> + <para> + This turns on the external 48MHz clock then switches the + hardware to using it. This is required by many of the + internal devices like USB. It should be called by the + 'main' function first, before initializing any of the + other devices in the system. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>Timer Functions</title> + <para> + AltOS sets up one of the cc1111 timers to run at 100Hz and + exposes this tick as the fundemental unit of time. At each + interrupt, AltOS increments the counter, and schedules any tasks + waiting for that time to pass, then fires off the ADC system to + collect current data readings. Doing this from the ISR ensures + that the ADC values are sampled at a regular rate, independent + of any scheduling jitter. + </para> + <variablelist> + <title>AltOS Timer Functions</title> + <varlistentry> + <term>ao_time</term> + <listitem> + <programlisting> +uint16_t +ao_time(void) + </programlisting> + <para> + Returns the current system tick count. Note that this is + only a 16 bit value, and so it wraps every 655.36 seconds. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_delay</term> + <listitem> + <programlisting> +void +ao_delay(uint16_t ticks); + </programlisting> + <para> + Suspend the current task for at least 'ticks' clock units. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_timer_set_adc_interval</term> + <listitem> + <programlisting> +void +ao_timer_set_adc_interval(uint8_t interval); + </programlisting> + <para> + This sets the number of ticks between ADC samples. If set + to 0, no ADC samples are generated. AltOS uses this to + slow down the ADC sampling rate to save power. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_timer_init</term> + <listitem> + <programlisting> +void +ao_timer_init(void) + </programlisting> + <para> + This turns on the 100Hz tick using the CC1111 timer 1. It + is required for any of the time-based functions to + work. It should be called by 'main' before ao_start_scheduler. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>AltOS Mutexes</title> + <para> + AltOS provides mutexes as a basic synchronization primitive. Each + mutexes is simply a byte of memory which holds 0 when the mutex + is free or the task id of the owning task when the mutex is + owned. Mutex calls are checked—attempting to acquire a mutex + already held by the current task or releasing a mutex not held + by the current task will both cause a panic. + </para> + <variablelist> + <title>Mutex Functions</title> + <varlistentry> + <term>ao_mutex_get</term> + <listitem> + <programlisting> +void +ao_mutex_get(__xdata uint8_t *mutex); + </programlisting> + <para> + Acquires the specified mutex, blocking if the mutex is + owned by another task. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_mutex_put</term> + <listitem> + <programlisting> +void +ao_mutex_put(__xdata uint8_t *mutex); + </programlisting> + <para> + Releases the specified mutex, waking up all tasks waiting + for it. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>CC1111 DMA engine</title> + <para> + The CC1111 contains a useful bit of extra hardware in the form + of five programmable DMA engines. They can be configured to copy + data in memory, or between memory and devices (or even between + two devices). AltOS exposes a general interface to this hardware + and uses it to handle radio and SPI data. + </para> + <para> + Code using a DMA engine should allocate one at startup + time. There is no provision to free them, and if you run out, + AltOS will simply panic. + </para> + <para> + During operation, the DMA engine is initialized with the + transfer parameters. Then it is started, at which point it + awaits a suitable event to start copying data. When copying data + from hardware to memory, that trigger event is supplied by the + hardware device. When copying data from memory to hardware, the + transfer is usually initiated by software. + </para> + <variablelist> + <title>AltOS DMA functions</title> + <varlistentry> + <term>ao_dma_alloc</term> + <listitem> + <programlisting> +uint8_t +ao_dma_alloc(__xdata uint8_t *done) + </programlisting> + <para> + Allocates a DMA engine, returning the identifier. Whenever + this DMA engine completes a transfer. 'done' is cleared + when the DMA is started, and then receives the + AO_DMA_DONE bit on a successful transfer or the + AO_DMA_ABORTED bit if ao_dma_abort was called. Note that + it is possible to get both bits if the transfer was + aborted after it had finished. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_dma_set_transfer</term> + <listitem> + <programlisting> +void +ao_dma_set_transfer(uint8_t id, + void __xdata *srcaddr, + void __xdata *dstaddr, + uint16_t count, + uint8_t cfg0, + uint8_t cfg1) + </programlisting> + <para> + Initializes the specified dma engine to copy data + from 'srcaddr' to 'dstaddr' for 'count' units. cfg0 and + cfg1 are values directly out of the CC1111 documentation + and tell the DMA engine what the transfer unit size, + direction and step are. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_dma_start</term> + <listitem> + <programlisting> +void +ao_dma_start(uint8_t id); + </programlisting> + <para> + Arm the specified DMA engine and await a signal from + either hardware or software to start transferring data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_dma_trigger</term> + <listitem> + <programlisting> +void +ao_dma_trigger(uint8_t id) + </programlisting> + <para> + Trigger the specified DMA engine to start copying data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_dma_abort</term> + <listitem> + <programlisting> +void +ao_dma_abort(uint8_t id) + </programlisting> + <para> + Terminate any in-progress DMA transation, marking its + 'done' variable with the AO_DMA_ABORTED bit. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>SDCC Stdio interface</title> + <para> + AltOS offers a stdio interface over both USB and the RF packet + link. This provides for control of the device localy or + remotely. This is hooked up to the stdio functions in SDCC by + providing the standard putchar/getchar/flush functions. These + automatically multiplex the two available communication + channels; output is always delivered to the channel which + provided the most recent input. + </para> + <variablelist> + <title>SDCC stdio functions</title> + <varlistentry> + <term>putchar</term> + <listitem> + <programlisting> +void +putchar(char c) + </programlisting> + <para> + Delivers a single character to the current console + device. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>getchar</term> + <listitem> + <programlisting> +char +getchar(void) + </programlisting> + <para> + Reads a single character from any of the available + console devices. The current console device is set to + that which delivered this character. This blocks until + a character is available. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>flush</term> + <listitem> + <programlisting> +void +flush(void) + </programlisting> + <para> + Flushes the current console device output buffer. Any + pending characters will be delivered to the target device. +xo </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_add_stdio</term> + <listitem> + <programlisting> +void +ao_add_stdio(char (*pollchar)(void), + void (*putchar)(char), + void (*flush)(void)) + </programlisting> + <para> + This adds another console device to the available + list. + </para> + <para> + 'pollchar' returns either an available character or + AO_READ_AGAIN if none is available. Significantly, it does + not block. The device driver must set 'ao_stdin_ready' to + 1 and call ao_wakeup(&ao_stdin_ready) when it receives + input to tell getchar that more data is available, at + which point 'pollchar' will be called again. + </para> + <para> + 'putchar' queues a character for output, flushing if the output buffer is + full. It may block in this case. + </para> + <para> + 'flush' forces the output buffer to be flushed. It may + block until the buffer is delivered, but it is not + required to do so. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>Command line interface</title> + <para> + AltOS includes a simple command line parser which is hooked up + to the stdio interfaces permitting remote control of the device + over USB or the RF link as desired. Each command uses a single + character to invoke it, the remaining characters on the line are + available as parameters to the command. + </para> + <variablelist> + <title>AltOS command line parsing functions</title> + <varlistentry> + <term>ao_cmd_register</term> + <listitem> + <programlisting> +void +ao_cmd_register(__code struct ao_cmds *cmds) + </programlisting> + <para> + This registers a set of commands with the command + parser. There is a fixed limit on the number of command + sets, the system will panic if too many are registered. + Each command is defined by a struct ao_cmds entry: + <programlisting> +struct ao_cmds { + char cmd; + void (*func)(void); + const char *help; +}; + </programlisting> + 'cmd' is the character naming the command. 'func' is the + function to invoke and 'help' is a string displayed by the + '?' command. Syntax errors found while executing 'func' + should be indicated by modifying the global ao_cmd_status + variable with one of the following values: + <variablelist> + <varlistentry> + <term>ao_cmd_success</term> + <listitem> + <para> + The command was parsed successfully. There is no + need to assign this value, it is the default. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_lex_error</term> + <listitem> + <para> + A token in the line was invalid, such as a number + containing invalid characters. The low-level + lexing functions already assign this value as needed. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_syntax_error</term> + <listitem> + <para> + The command line is invalid for some reason other + than invalid tokens. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_lex</term> + <listitem> + <programlisting> +void +ao_cmd_lex(void); + </programlisting> + <para> + This gets the next character out of the command line + buffer and sticks it into ao_cmd_lex_c. At the end of the + line, ao_cmd_lex_c will get a newline ('\n') character. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_put16</term> + <listitem> + <programlisting> +void +ao_cmd_put16(uint16_t v); + </programlisting> + <para> + Writes 'v' as four hexadecimal characters. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_put8</term> + <listitem> + <programlisting> +void +ao_cmd_put8(uint8_t v); + </programlisting> + <para> + Writes 'v' as two hexadecimal characters. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_white</term> + <listitem> + <programlisting> +void +ao_cmd_white(void) + </programlisting> + <para> + This skips whitespace by calling ao_cmd_lex while + ao_cmd_lex_c is either a space or tab. It does not skip + any characters if ao_cmd_lex_c already non-white. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_hex</term> + <listitem> + <programlisting> +void +ao_cmd_hex(void) + </programlisting> + <para> + This reads a 16-bit hexadecimal value from the command + line with optional leading whitespace. The resulting value + is stored in ao_cmd_lex_i; + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_decimal</term> + <listitem> + <programlisting> +void +ao_cmd_decimal(void) + </programlisting> + <para> + This reads a 32-bit decimal value from the command + line with optional leading whitespace. The resulting value + is stored in ao_cmd_lex_u32 and the low 16 bits are stored + in ao_cmd_lex_i; + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_match_word</term> + <listitem> + <programlisting> +uint8_t +ao_match_word(__code char *word) + </programlisting> + <para> + This checks to make sure that 'word' occurs on the command + line. It does not skip leading white space. If 'word' is + found, then 1 is returned. Otherwise, ao_cmd_status is set to + ao_cmd_syntax_error and 0 is returned. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_cmd_init</term> + <listitem> + <programlisting> +void +ao_cmd_init(void + </programlisting> + <para> + Initializes the command system, setting up the built-in + commands and adding a task to run the command processing + loop. It should be called by 'main' before ao_start_scheduler. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>CC1111 USB target device</title> + <para> + The CC1111 contains a full-speed USB target device. It can be + programmed to offer any kind of USB target, but to simplify + interactions with a variety of operating systems, AltOS provides + only a single target device profile, that of a USB modem which + has native drivers for Linux, Windows and Mac OS X. It would be + easy to change the code to provide an alternate target device if + necessary. + </para> + <para> + To the rest of the system, the USB device looks like a simple + two-way byte stream. It can be hooked into the command line + interface if desired, offering control of the device over the + USB link. Alternatively, the functions can be accessed directly + to provide for USB-specific I/O. + </para> + <variablelist> + <title>AltOS USB functions</title> + <varlistentry> + <term>ao_usb_flush</term> + <listitem> + <programlisting> +void +ao_usb_flush(void); + </programlisting> + <para> + Flushes any pending USB output. This queues an 'IN' packet + to be delivered to the USB host if there is pending data, + or if the last IN packet was full to indicate to the host + that there isn't any more pending data available. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_putchar</term> + <listitem> + <programlisting> +void +ao_usb_putchar(char c); + </programlisting> + <para> + If there is a pending 'IN' packet awaiting delivery to the + host, this blocks until that has been fetched. Then, this + adds a byte to the pending IN packet for delivery to the + USB host. If the USB packet is full, this queues the 'IN' + packet for delivery. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_pollchar</term> + <listitem> + <programlisting> +char +ao_usb_pollchar(void); + </programlisting> + <para> + If there are no characters remaining in the last 'OUT' + packet received, this returns AO_READ_AGAIN. Otherwise, it + returns the next character, reporting to the host that it + is ready for more data when the last character is gone. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_getchar</term> + <listitem> + <programlisting> +char +ao_usb_getchar(void); + </programlisting> + <para> + This uses ao_pollchar to receive the next character, + blocking while ao_pollchar returns AO_READ_AGAIN. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_disable</term> + <listitem> + <programlisting> +void +ao_usb_disable(void); + </programlisting> + <para> + This turns off the USB controller. It will no longer + respond to host requests, nor return characters. Calling + any of the i/o routines while the USB device is disabled + is undefined, and likely to break things. Disabling the + USB device when not needed saves power. + </para> + <para> + Note that neither TeleDongle nor TeleMetrum are able to + signal to the USB host that they have disconnected, so + after disabling the USB device, it's likely that the cable + will need to be disconnected and reconnected before it + will work again. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_enable</term> + <listitem> + <programlisting> +void +ao_usb_enable(void); + </programlisting> + <para> + This turns the USB controller on again after it has been + disabled. See the note above about needing to physically + remove and re-insert the cable to get the host to + re-initialize the USB link. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_usb_init</term> + <listitem> + <programlisting> +void +ao_usb_init(void); + </programlisting> + <para> + This turns the USB controller on, adds a task to handle + the control end point and adds the usb I/O functions to + the stdio system. Call this from main before + ao_start_scheduler. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>CC1111 Serial peripheral</title> + <para> + The CC1111 provides two USART peripherals. AltOS uses one for + asynch serial data, generally to communicate with a GPS device, + and the other for a SPI bus. The UART is configured to operate + in 8-bits, no parity, 1 stop bit framing. The default + configuration has clock settings for 4800, 9600 and 57600 baud + operation. Additional speeds can be added by computing + appropriate clock values. + </para> + <para> + To prevent loss of data, AltOS provides receive and transmit + fifos of 32 characters each. + </para> + <variablelist> + <title>AltOS serial functions</title> + <varlistentry> + <term>ao_serial_getchar</term> + <listitem> + <programlisting> +char +ao_serial_getchar(void); + </programlisting> + <para> + Returns the next character from the receive fifo, blocking + until a character is received if the fifo is empty. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_serial_putchar</term> + <listitem> + <programlisting> +void +ao_serial_putchar(char c); + </programlisting> + <para> + Adds a character to the transmit fifo, blocking if the + fifo is full. Starts transmitting characters. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_serial_drain</term> + <listitem> + <programlisting> +void +ao_serial_drain(void); + </programlisting> + <para> + Blocks until the transmit fifo is empty. Used internally + when changing serial speeds. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_serial_set_speed</term> + <listitem> + <programlisting> +void +ao_serial_set_speed(uint8_t speed); + </programlisting> + <para> + Changes the serial baud rate to one of + AO_SERIAL_SPEED_4800, AO_SERIAL_SPEED_9600 or + AO_SERIAL_SPEED_57600. This first flushes the transmit + fifo using ao_serial_drain. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_serial_init</term> + <listitem> + <programlisting> +void +ao_serial_init(void) + </programlisting> + <para> + Initializes the serial peripheral. Call this from 'main' + before jumping to ao_start_scheduler. The default speed + setting is AO_SERIAL_SPEED_4800. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> + <chapter> + <title>CC1111 Radio peripheral</title> + <para> + The CC1111 radio transceiver sends and receives digital packets + with forward error correction and detection. The AltOS driver is + fairly specific to the needs of the TeleMetrum and TeleDongle + devices, using it for other tasks may require customization of + the driver itself. There are three basic modes of operation: + <orderedlist> + <listitem> + <para> + Telemetry mode. In this mode, TeleMetrum transmits telemetry + frames at a fixed rate. The frames are of fixed size. This + is strictly a one-way communication from TeleMetrum to + TeleDongle. + </para> + </listitem> + <listitem> + <para> + Packet mode. In this mode, the radio is used to create a + reliable duplex byte stream between TeleDongle and + TeleMetrum. This is an asymmetrical protocol with + TeleMetrum only transmitting in response to a packet sent + from TeleDongle. Thus getting data from TeleMetrum to + TeleDongle requires polling. The polling rate is adaptive, + when no data has been received for a while, the rate slows + down. The packets are checked at both ends and invalid + data are ignored. + </para> + <para> + On the TeleMetrum side, the packet link is hooked into the + stdio mechanism, providing an alternate data path for the + command processor. It is enabled when the unit boots up in + 'idle' mode. + </para> + <para> + On the TeleDongle side, the packet link is enabled with a + command; data from the stdio package is forwarded over the + packet link providing a connection from the USB command + stream to the remote TeleMetrum device. + </para> + </listitem> + <listitem> + <para> + Radio Direction Finding mode. In this mode, TeleMetrum + constructs a special packet that sounds like an audio tone + when received by a conventional narrow-band FM + receiver. This is designed to provide a beacon to track + the device when other location mechanisms fail. + </para> + </listitem> + </orderedlist> + </para> + <variablelist> + <title>AltOS radio functions</title> + <varlistentry> + <term>ao_radio_set_telemetry</term> + <listitem> + <programlisting> +void +ao_radio_set_telemetry(void); + </programlisting> + <para> + Configures the radio to send or receive telemetry + packets. This includes packet length, modulation scheme and + other RF parameters. It does not include the base frequency + or channel though. Those are set at the time of transmission + or reception, in case the values are changed by the user. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_set_packet</term> + <listitem> + <programlisting> +void +ao_radio_set_packet(void); + </programlisting> + <para> + Configures the radio to send or receive packet data. This + includes packet length, modulation scheme and other RF + parameters. It does not include the base frequency or + channel though. Those are set at the time of transmission or + reception, in case the values are changed by the user. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_set_rdf</term> + <listitem> + <programlisting> +void +ao_radio_set_rdf(void); + </programlisting> + <para> + Configures the radio to send RDF 'packets'. An RDF 'packet' + is a sequence of hex 0x55 bytes sent at a base bit rate of + 2kbps using a 5kHz deviation. All of the error correction + and data whitening logic is turned off so that the resulting + modulation is received as a 1kHz tone by a conventional 70cm + FM audio receiver. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_idle</term> + <listitem> + <programlisting> +void +ao_radio_idle(void); + </programlisting> + <para> + Sets the radio device to idle mode, waiting until it reaches + that state. This will terminate any in-progress transmit or + receive operation. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_get</term> + <listitem> + <programlisting> +void +ao_radio_get(void); + </programlisting> + <para> + Acquires the radio mutex and then configures the radio + frequency using the global radio calibration and channel + values. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_put</term> + <listitem> + <programlisting> +void +ao_radio_put(void); + </programlisting> + <para> + Releases the radio mutex. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_abort</term> + <listitem> + <programlisting> +void +ao_radio_abort(void); + </programlisting> + <para> + Aborts any transmission or reception process by aborting the + associated DMA object and calling ao_radio_idle to terminate + the radio operation. + </para> + </listitem> + </varlistentry> + </variablelist> + <variablelist> + <title>AltOS radio telemetry functions</title> + <para> + In telemetry mode, you can send or receive a telemetry + packet. The data from receiving a packet also includes the RSSI + and status values supplied by the receiver. These are added + after the telemetry data. + </para> + <varlistentry> + <term>ao_radio_send</term> + <listitem> + <programlisting> +void +ao_radio_send(__xdata struct ao_telemetry *telemetry); + </programlisting> + <para> + This sends the specific telemetry packet, waiting for the + transmission to complete. The radio must have been set to + telemetry mode. This function calls ao_radio_get() before + sending, and ao_radio_put() afterwards, to correctly + serialize access to the radio device. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_radio_recv</term> + <listitem> + <programlisting> +void +ao_radio_recv(__xdata struct ao_radio_recv *radio); + </programlisting> + <para> + This blocks waiting for a telemetry packet to be received. + The radio must have been set to telemetry mode. This + function calls ao_radio_get() before receiving, and + ao_radio_put() afterwards, to correctly serialize access + to the radio device. This returns non-zero if a packet was + received, or zero if the operation was aborted (from some + other task calling ao_radio_abort()). + </para> + </listitem> + </varlistentry> + </variablelist> + <variablelist> + <title>AltOS radio direction finding function</title> + <para> + In radio direction finding mode, there's just one function to + use + </para> + <varlistentry> + <term>ao_radio_rdf</term> + <listitem> + <programlisting> +void +ao_radio_rdf(int ms); + </programlisting> + <para> + This sends an RDF packet lasting for the specified amount + of time. The maximum length is 1020 ms. + </para> + </listitem> + </varlistentry> + </variablelist> + <variablelist> + <title>Packet mode functions</title> + <para> + Packet mode is asymmetrical and is configured at compile time + for either master or slave mode (but not both). The basic I/O + functions look the same at both ends, but the internals are + different, along with the initialization steps. + </para> + <varlistentry> + <term>ao_packet_putchar</term> + <listitem> + <programlisting> +void +ao_packet_putchar(char c); + </programlisting> + <para> + If the output queue is full, this first blocks waiting for + that data to be delivered. Then, queues a character for + packet transmission. On the master side, this will + transmit a packet if the output buffer is full. On the + slave side, any pending data will be sent the next time + the master polls for data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_packet_pollchar</term> + <listitem> + <programlisting> +char +ao_packet_pollchar(void); + </programlisting> + <para> + This returns a pending input character if available, + otherwise returns AO_READ_AGAIN. On the master side, if + this empties the buffer, it triggers a poll for more data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_packet_slave_start</term> + <listitem> + <programlisting> +void +ao_packet_slave_start(void); + </programlisting> + <para> + This is available only on the slave side and starts a task + to listen for packet data. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_packet_slave_stop</term> + <listitem> + <programlisting> +void +ao_packet_slave_stop(void); + </programlisting> + <para> + Disables the packet slave task, stopping the radio receiver. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_packet_slave_init</term> + <listitem> + <programlisting> +void +ao_packet_slave_init(void); + </programlisting> + <para> + Adds the packet stdio functions to the stdio package so + that when packet slave mode is enabled, characters will + get send and received through the stdio functions. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>ao_packet_master_init</term> + <listitem> + <programlisting> +void +ao_packet_master_init(void); + </programlisting> + <para> + Adds the 'p' packet forward command to start packet mode. + </para> + </listitem> + </varlistentry> + </variablelist> + </chapter> +</book> diff --git a/doc/telemetrum-doc.xsl b/doc/telemetrum-doc.xsl index 90e202b4..e75e10b5 100644 --- a/doc/telemetrum-doc.xsl +++ b/doc/telemetrum-doc.xsl @@ -28,21 +28,9 @@ </legalnotice> <revhistory> <revision> - <revnumber>0.3</revnumber> - <date>12 November 2010</date> - <revremark> - Add instructions for re-flashing devices using AltosUI - </revremark> - </revision> - <revision> - <revnumber>0.2</revnumber> - <date>18 July 2010</date> - <revremark>Significant update</revremark> - </revision> - <revision> - <revnumber>0.1</revnumber> - <date>30 March 2010</date> - <revremark>Initial content</revremark> + <revnumber>0.8</revnumber> + <date>24 November 2010</date> + <revremark>Updated for software version 0.8 </revremark> </revision> </revhistory> </bookinfo> @@ -72,8 +60,8 @@ </para> <para> More products will be added to the Altus Metrum family over time, and - we currently envision that this will evolve to be be the single, - comprehensive manual for the entire product family. + we currently envision that this will be a single, comprehensive manual + for the entire product family. </para> </chapter> <chapter> @@ -88,33 +76,34 @@ <para> The first thing to do after you check the inventory of parts in your "starter kit" is to charge the battery by plugging it into the - corresponding socket of the TeleMetrum and then using the USB A to mini - B cable to plug the Telemetrum into your computer's USB socket. The + corresponding socket of the TeleMetrum and then using the USB A to +mini B + cable to plug the Telemetrum into your computer's USB socket. The TeleMetrum circuitry will charge the battery whenever it is plugged - in, because the TeleMetrum's on-off switch does NOT control the + in, because the TeleMetrum's on-off switch does NOT control the charging circuitry. When the GPS chip is initially searching for - satellites, TeleMetrum will consume more current than it can pull + satellites, TeleMetrum will consume more current than it can pull from the usb port, so the battery must be attached in order to get - satellite lock. Once GPS is locked, the current consumption goes back + satellite lock. Once GPS is locked, the current consumption goes back down enough to enable charging while running. So it's a good idea to fully charge the battery as your first item of business so there is no issue getting and maintaining satellite lock. The yellow charge indicator led will go out when the - battery is nearly full and the charger goes to trickle charge. It - can take several hours to fully recharge a deeply discharged battery. + battery is nearly full and the charger goes to trickle charge. It + can takeseveral hours to fully recharge a deeply discharged battery. </para> <para> The other active device in the starter kit is the TeleDongle USB to - RF interface. If you plug it in to your Mac or Linux computer it should + RF interface. If you plug it in to your Mac or Linux computer it should "just work", showing up as a serial port device. Windows systems need driver information that is part of the AltOS download to know that the existing USB modem driver will work. If you are using Linux and are having problems, try moving to a fresher kernel (2.6.33 or newer), as - the USB serial driver had ugly bugs in some earlier versions. + the USB serial driver had ugly bugs in some earlier versions. </para> <para> Next you should obtain and install the AltOS utilities. These include - the AltosUI ground station program, current firmware images for + the AltosUI ground station program, current firmware images for TeleMetrum and TeleDongle, and a number of standalone utilities that are rarely needed. Pre-built binary packages are available for Debian Linux, Microsoft Windows, and recent MacOSX versions. Full sourcecode @@ -467,7 +456,7 @@ </para> </chapter> <chapter> - <title>Operation</title> + <title>System Operation</title> <section> <title>Firmware Modes </title> <para> @@ -753,24 +742,26 @@ calibration is poor. </para> <para> - In the unlikely event an accel cal that goes badly, it is possible - that TeleMetrum may always come up in 'pad mode' and as such not be - listening to either the USB or radio interfaces. If that happens, - there is a special hook in the firmware to force the board back - in to 'idle mode' so you can re-do the cal. To use this hook, you - just need to ground the SPI clock pin at power-on. This pin is - available as pin 2 on the 8-pin companion connector, and pin 1 is - ground. So either carefully install a fine-gauge wire jumper - between the two pins closest to the index hole end of the 8-pin - connector, or plug in the programming cable to the 8-pin connector - and use a small screwdriver or similar to short the two pins closest - to the index post on the 4-pin end of the programming cable, and - power up the board. It should come up in 'idle mode' (two beeps). + In the unlikely event an accel cal that goes badly, it is possible + that TeleMetrum may always come up in 'pad mode' and as such not be + listening to either the USB or radio interfaces. If that happens, + there is a special hook in the firmware to force the board back + in to 'idle mode' so you can re-do the cal. To use this hook, you + just need to ground the SPI clock pin at power-on. This pin is + available as pin 2 on the 8-pin companion connector, and pin 1 is + ground. So either carefully install a fine-gauge wire jumper + between the two pins closest to the index hole end of the 8-pin + connector, or plug in the programming cable to the 8-pin connector + and use a small screwdriver or similar to short the two pins closest + to the index post on the 4-pin end of the programming cable, and + power up the board. It should come up in 'idle mode' (two beeps). </para> </section> </section> - </chapter> - <chapter> + + + + <section> <title>Updating Device Firmware</title> <para> The big conceptual thing to realize is that you have to use a @@ -931,6 +922,615 @@ used in a rocket don't ever come loose accidentally in flight. </para> </section> + </section> + + + + </chapter> + <chapter> + + <title>AltosUI</title> + <para> + The AltosUI program provides a graphical user interface for + interacting with the Altus Metrum product family, including + TeleMetrum and TeleDongle. AltosUI can monitor telemetry data, + configure TeleMetrum and TeleDongle devices and many other + tasks. The primary interface window provides a selection of + buttons, one for each major activity in the system. This manual + is split into chapters, each of which documents one of the tasks + provided from the top-level toolbar. + </para> + <section> + <title>Packet Command Mode</title> + <subtitle>Controlling TeleMetrum Over The Radio Link</subtitle> + <para> + One of the unique features of the Altos Metrum environment is + the ability to create a two way command link between TeleDongle + and TeleMetrum using the digital radio transceivers built into + each device. This allows you to interact with TeleMetrum from + afar, as if it were directly connected to the computer. + </para> + <para> + Any operation which can be performed with TeleMetrum + can either be done with TeleMetrum directly connected to + the computer via the USB cable, or through the packet + link. Simply select the appropriate TeleDongle device when + the list of devices is presented and AltosUI will use packet + command mode. + </para> + <itemizedlist> + <listitem> + <para> + Save Flight Data—Recover flight data from the rocket without + opening it up. + </para> + </listitem> + <listitem> + <para> + Configure TeleMetrum—Reset apogee delays or main deploy + heights to respond to changing launch conditions. You can + also 'reboot' the TeleMetrum device. Use this to remotely + enable the flight computer by turning TeleMetrum on while + horizontal, then once the airframe is oriented for launch, + you can reboot TeleMetrum and have it restart in pad mode + without having to climb the scary ladder. + </para> + </listitem> + <listitem> + <para> + Fire Igniters—Test your deployment charges without snaking + wires out through holes in the airframe. Simply assembly the + rocket as if for flight with the apogee and main charges + loaded, then remotely command TeleMetrum to fire the + igniters. + </para> + </listitem> + </itemizedlist> + <para> + Packet command mode uses the same RF channels as telemetry + mode. Configure the desired TeleDongle channel using the + flight monitor window channel selector and then close that + window before performing the desired operation. + </para> + <para> + TeleMetrum only enables packet command mode in 'idle' mode, so + make sure you have TeleMetrum lying horizontally when you turn + it on. Otherwise, TeleMetrum will start in 'pad' mode ready for + flight and will not be listening for command packets from TeleDongle. + </para> + <para> + When packet command mode is enabled, you can monitor the link + by watching the lights on the TeleDongle and TeleMetrum + devices. The red LED will flash each time TeleDongle or + TeleMetrum transmit a packet while the green LED will light up + on TeleDongle while it is waiting to receive a packet from + TeleMetrum. + </para> + </section> + <section> + <title>Monitor Flight</title> + <subtitle>Receive, Record and Display Telemetry Data</subtitle> + <para> + Selecting this item brings up a dialog box listing all of the + connected TeleDongle devices. When you choose one of these, + AltosUI will create a window to display telemetry data as + received by the selected TeleDongle device. + </para> + <para> + All telemetry data received are automatically recorded in + suitable log files. The name of the files includes the current + date and rocket serial and flight numbers. + </para> + <para> + The radio channel being monitored by the TeleDongle device is + displayed at the top of the window. You can configure the + channel by clicking on the channel box and selecting the desired + channel. AltosUI remembers the last channel selected for each + TeleDongle and selects that automatically the next time you use + that device. + </para> + <para> + Below the TeleDongle channel selector, the window contains a few + significant pieces of information about the TeleMetrum providing + the telemetry data stream: + </para> + <itemizedlist> + <listitem> + <para>The TeleMetrum callsign</para> + </listitem> + <listitem> + <para>The TeleMetrum serial number</para> + </listitem> + <listitem> + <para>The flight number. Each TeleMetrum remembers how many + times it has flown. + </para> + </listitem> + <listitem> + <para> + The rocket flight state. Each flight passes through several + states including Pad, Boost, Fast, Coast, Drogue, Main and + Landed. + </para> + </listitem> + <listitem> + <para> + The Received Signal Strength Indicator value. This lets + you know how strong a signal TeleDongle is receiving. The + radio inside TeleDongle operates down to about -99dBm; + weaker signals may not be receiveable. The packet link uses + error correction and detection techniques which prevent + incorrect data from being reported. + </para> + </listitem> + </itemizedlist> + <para> + Finally, the largest portion of the window contains a set of + tabs, each of which contain some information about the rocket. + They're arranged in 'flight order' so that as the flight + progresses, the selected tab automatically switches to display + data relevant to the current state of the flight. You can select + other tabs at any time. The final 'table' tab contains all of + the telemetry data in one place. + </para> + <section> + <title>Launch Pad</title> + <para> + The 'Launch Pad' tab shows information used to decide when the + rocket is ready for flight. The first elements include red/green + indicators, if any of these is red, you'll want to evaluate + whether the rocket is ready to launch: + <itemizedlist> + <listitem> + <para> + Battery Voltage. This indicates whether the LiPo battery + powering the TeleMetrum has sufficient charge to last for + the duration of the flight. A value of more than + 3.7V is required for a 'GO' status. + </para> + </listitem> + <listitem> + <para> + Apogee Igniter Voltage. This indicates whether the apogee + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the LiPo battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + <listitem> + <para> + Main Igniter Voltage. This indicates whether the main + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the LiPo battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + <listitem> + <para> + GPS Locked. This indicates whether the GPS receiver is + currently able to compute position information. GPS requires + at least 4 satellites to compute an accurate position. + </para> + </listitem> + <listitem> + <para> + GPS Ready. This indicates whether GPS has reported at least + 10 consecutive positions without losing lock. This ensures + that the GPS receiver has reliable reception from the + satellites. + </para> + </listitem> + </itemizedlist> + <para> + The LaunchPad tab also shows the computed launch pad position + and altitude, averaging many reported positions to improve the + accuracy of the fix. + </para> + </para> + </section> + <section> + <title>Ascent</title> + <para> + This tab is shown during Boost, Fast and Coast + phases. The information displayed here helps monitor the + rocket as it heads towards apogee. + </para> + <para> + The height, speed and acceleration are shown along with the + maxium values for each of them. This allows you to quickly + answer the most commonly asked questions you'll hear during + flight. + </para> + <para> + The current latitude and longitude reported by the GPS are + also shown. Note that under high acceleration, these values + may not get updated as the GPS receiver loses position + fix. Once the rocket starts coasting, the receiver should + start reporting position again. + </para> + <para> + Finally, the current igniter voltages are reported as in the + Launch Pad tab. This can help diagnose deployment failures + caused by wiring which comes loose under high acceleration. + </para> + </section> + <section> + <title>Descent</title> + <para> + Once the rocket has reached apogee and (we hope) activated the + apogee charge, attention switches to tracking the rocket on + the way back to the ground, and for dual-deploy flights, + waiting for the main charge to fire. + </para> + <para> + To monitor whether the apogee charge operated correctly, the + current descent rate is reported along with the current + height. Good descent rates generally range from 15-30m/s. + </para> + <para> + To help locate the rocket in the sky, use the elevation and + bearing information to figure out where to look. Elevation is + in degrees above the horizon. Bearing is reported in degrees + relative to true north. Range can help figure out how big the + rocket will appear. Note that all of these values are relative + to the pad location. If the elevation is near 90°, the rocket + is over the pad, not over you. + </para> + <para> + Finally, the igniter voltages are reported in this tab as + well, both to monitor the main charge as well as to see what + the status of the apogee charge is. + </para> + </section> + <section> + <title>Landed</title> + <para> + Once the rocket is on the ground, attention switches to + recovery. While the radio signal is generally lost once the + rocket is on the ground, the last reported GPS position is + generally within a short distance of the actual landing location. + </para> + <para> + The last reported GPS position is reported both by + latitude and longitude as well as a bearing and distance from + the launch pad. The distance should give you a good idea of + whether you'll want to walk or hitch a ride. Take the reported + latitude and longitude and enter them into your handheld GPS + unit and have that compute a track to the landing location. + </para> + <para> + Finally, the maximum height, speed and acceleration reported + during the flight are displayed for your admiring observers. + </para> + </section> + <section> + <title>Site Map</title> + <para> + When the rocket gets a GPS fix, the Site Map tab will map + the rocket's position to make it easier for you to locate the + rocket, both while it is in the air, and when it has landed. The + rocket's state is indicated by colour: white for pad, red for + boost, pink for fast, yellow for coast, light blue for drogue, + dark blue for main, and black for landed. + </para> + <para> + The map's scale is approximately 3m (10ft) per pixel. The map + can be dragged using the left mouse button. The map will attempt + to keep the rocket roughly centred while data is being received. + </para> + <para> + Images are fetched automatically via the Google Maps Static API, + and are cached for reuse. If map images cannot be downloaded, + the rocket's path will be traced on a dark grey background + instead. + </para> + </section> + </section> + <section> + <title>Save Flight Data</title> + <para> + TeleMetrum records flight data to its internal flash memory. + This data is recorded at a much higher rate than the telemetry + system can handle, and is not subject to radio drop-outs. As + such, it provides a more complete and precise record of the + flight. The 'Save Flight Data' button allows you to read the + flash memory and write it to disk. + </para> + <para> + Clicking on the 'Save Flight Data' button brings up a list of + connected TeleMetrum and TeleDongle devices. If you select a + TeleMetrum device, the flight data will be downloaded from that + device directly. If you select a TeleDongle device, flight data + will be downloaded from a TeleMetrum device connected via the + packet command link to the specified TeleDongle. See the chapter + on Packet Command Mode for more information about this. + </para> + <para> + The filename for the data is computed automatically from the recorded + flight date, TeleMetrum serial number and flight number + information. + </para> + </section> + <section> + <title>Replay Flight</title> + <para> + Select this button and you are prompted to select a flight + record file, either a .telem file recording telemetry data or a + .eeprom file containing flight data saved from the TeleMetrum + flash memory. + </para> + <para> + Once a flight record is selected, the flight monitor interface + is displayed and the flight is re-enacted in real time. Check + the Monitor Flight chapter above to learn how this window operates. + </para> + </section> + <section> + <title>Graph Data</title> + <para> + Select this button and you are prompted to select a flight + record file, either a .telem file recording telemetry data or a + .eeprom file containing flight data saved from the TeleMetrum + flash memory. + </para> + <para> + Once a flight record is selected, the acceleration (blue), + velocity (green) and altitude (red) of the flight are plotted and + displayed, measured in metric units. + </para> + <para> + The graph can be zoomed into a particular area by clicking and + dragging down and to the right. Once zoomed, the graph can be + reset by clicking and dragging up and to the left. Holding down + control and clicking and dragging allows the graph to be panned. + The right mouse button causes a popup menu to be displayed, giving + you the option save or print the plot. + </para> + <para> + Note that telemetry files will generally produce poor graphs + due to the lower sampling rate and missed telemetry packets, + and will also often have significant amounts of data received + while the rocket was waiting on the pad. Use saved flight data + for graphing where possible. + </para> + </section> + <section> + <title>Export Data</title> + <para> + This tool takes the raw data files and makes them available for + external analysis. When you select this button, you are prompted to select a flight + data file (either .eeprom or .telem will do, remember that + .eeprom files contain higher resolution and more continuous + data). Next, a second dialog appears which is used to select + where to write the resulting file. It has a selector to choose + between CSV and KML file formats. + </para> + <section> + <title>Comma Separated Value Format</title> + <para> + This is a text file containing the data in a form suitable for + import into a spreadsheet or other external data analysis + tool. The first few lines of the file contain the version and + configuration information from the TeleMetrum device, then + there is a single header line which labels all of the + fields. All of these lines start with a '#' character which + most tools can be configured to skip over. + </para> + <para> + The remaining lines of the file contain the data, with each + field separated by a comma and at least one space. All of + the sensor values are converted to standard units, with the + barometric data reported in both pressure, altitude and + height above pad units. + </para> + </section> + <section> + <title>Keyhole Markup Language (for Google Earth)</title> + <para> + This is the format used by + Googleearth to provide an overlay within that + application. With this, you can use Googleearth to see the + whole flight path in 3D. + </para> + </section> + </section> + <section> + <title>Configure TeleMetrum</title> + <para> + Select this button and then select either a TeleMetrum or + TeleDongle Device from the list provided. Selecting a TeleDongle + device will use Packet Comamnd Mode to configure remote + TeleMetrum device. Learn how to use this in the Packet Command + Mode chapter. + </para> + <para> + The first few lines of the dialog provide information about the + connected TeleMetrum device, including the product name, + software version and hardware serial number. Below that are the + individual configuration entries. + </para> + <para> + At the bottom of the dialog, there are four buttons: + </para> + <itemizedlist> + <listitem> + <para> + Save. This writes any changes to the TeleMetrum + configuration parameter block in flash memory. If you don't + press this button, any changes you make will be lost. + </para> + </listitem> + <listitem> + <para> + Reset. This resets the dialog to the most recently saved values, + erasing any changes you have made. + </para> + </listitem> + <listitem> + <para> + Reboot. This reboots the TeleMetrum device. Use this to + switch from idle to pad mode by rebooting once the rocket is + oriented for flight. + </para> + </listitem> + <listitem> + <para> + Close. This closes the dialog. Any unsaved changes will be + lost. + </para> + </listitem> + </itemizedlist> + <para> + The rest of the dialog contains the parameters to be configured. + </para> + <section> + <title>Main Deploy Altitude</title> + <para> + This sets the altitude (above the recorded pad altitude) at + which the 'main' igniter will fire. The drop-down menu shows + some common values, but you can edit the text directly and + choose whatever you like. If the apogee charge fires below + this altitude, then the main charge will fire two seconds + after the apogee charge fires. + </para> + </section> + <section> + <title>Apogee Delay</title> + <para> + When flying redundant electronics, it's often important to + ensure that multiple apogee charges don't fire at precisely + the same time as that can overpressurize the apogee deployment + bay and cause a structural failure of the airframe. The Apogee + Delay parameter tells the flight computer to fire the apogee + charge a certain number of seconds after apogee has been + detected. + </para> + </section> + <section> + <title>Radio Channel</title> + <para> + This configures which of the 10 radio channels to use for both + telemetry and packet command mode. Note that if you set this + value via packet command mode, you will have to reconfigure + the TeleDongle channel before you will be able to use packet + command mode again. + </para> + </section> + <section> + <title>Radio Calibration</title> + <para> + The radios in every Altus Metrum device are calibrated at the + factory to ensure that they transmit and receive on the + specified frequency for each channel. You can adjust that + calibration by changing this value. To change the TeleDongle's + calibration, you must reprogram the unit completely. + </para> + </section> + <section> + <title>Callsign</title> + <para> + This sets the callsign included in each telemetry packet. Set this + as needed to conform to your local radio regulations. + </para> + </section> + </section> + <section> + <title>Configure AltosUI</title> + <para> + This button presents a dialog so that you can configure the AltosUI global settings. + </para> + <section> + <title>Voice Settings</title> + <para> + AltosUI provides voice annoucements during flight so that you + can keep your eyes on the sky and still get information about + the current flight status. However, sometimes you don't want + to hear them. + </para> + <itemizedlist> + <listitem> + <para>Enable—turns all voice announcements on and off</para> + </listitem> + <listitem> + <para> + Test Voice—Plays a short message allowing you to verify + that the audio systme is working and the volume settings + are reasonable + </para> + </listitem> + </itemizedlist> + </section> + <section> + <title>Log Directory</title> + <para> + AltosUI logs all telemetry data and saves all TeleMetrum flash + data to this directory. This directory is also used as the + staring point when selecting data files for display or export. + </para> + <para> + Click on the directory name to bring up a directory choosing + dialog, select a new directory and click 'Select Directory' to + change where AltosUI reads and writes data files. + </para> + </section> + <section> + <title>Callsign</title> + <para> + This value is used in command packet mode and is transmitted + in each packet sent from TeleDongle and received from + TeleMetrum. It is not used in telemetry mode as that transmits + packets only from TeleMetrum to TeleDongle. Configure this + with the AltosUI operators callsign as needed to comply with + your local radio regulations. + </para> + </section> + </section> + <section> + <title>Flash Image</title> + <para> + This reprograms any Altus Metrum device by using a TeleMetrum or + TeleDongle as a programming dongle. Please read the directions + for connecting the programming cable in the main TeleMetrum + manual before reading these instructions. + </para> + <para> + Once you have the programmer and target devices connected, + push the 'Flash Image' button. That will present a dialog box + listing all of the connected devices. Carefully select the + programmer device, not the device to be programmed. + </para> + <para> + Next, select the image to flash to the device. These are named + with the product name and firmware version. The file selector + will start in the directory containing the firmware included + with the AltosUI package. Navigate to the directory containing + the desired firmware if it isn't there. + </para> + <para> + Next, a small dialog containing the device serial number and + RF calibration values should appear. If these values are + incorrect (possibly due to a corrupted image in the device), + enter the correct values here. + </para> + <para> + Finally, a dialog containing a progress bar will follow the + programming process. + </para> + <para> + When programming is complete, the target device will + reboot. Note that if the target device is connected via USB, you + will have to unplug it and then plug it back in for the USB + connection to reset so that you can communicate with the device + again. + </para> + </section> + <section> + <title>Fire Igniter</title> + <para> + </para> + </section> </chapter> <chapter> <title>Using Altus Metrum Products</title> @@ -1067,14 +1667,6 @@ </para> </section> </section> - <section> - <title> - How GPS Works - </title> - <para> - Placeholder. - </para> - </section> </chapter> </book> diff --git a/icon/grayled.png b/icon/grayled.png Binary files differnew file mode 100644 index 00000000..bb6005c6 --- /dev/null +++ b/icon/grayled.png diff --git a/icon/grayon.png b/icon/grayon.png Binary files differnew file mode 100644 index 00000000..c99b376e --- /dev/null +++ b/icon/grayon.png diff --git a/icon/greenled.png b/icon/greenled.png Binary files differnew file mode 100644 index 00000000..d7663961 --- /dev/null +++ b/icon/greenled.png diff --git a/icon/greenoff.png b/icon/greenoff.png Binary files differnew file mode 100644 index 00000000..c3cf8491 --- /dev/null +++ b/icon/greenoff.png diff --git a/icon/redled.png b/icon/redled.png Binary files differnew file mode 100644 index 00000000..230afae0 --- /dev/null +++ b/icon/redled.png diff --git a/icon/redoff.png b/icon/redoff.png Binary files differnew file mode 100644 index 00000000..a251402f --- /dev/null +++ b/icon/redoff.png diff --git a/src/ao_ignite.c b/src/ao_ignite.c index 4093b6a7..f2b15dd2 100644 --- a/src/ao_ignite.c +++ b/src/ao_ignite.c @@ -101,7 +101,6 @@ void ao_igniter(void) { __xdata enum ao_ignter igniter; - __xdata enum ao_igniter_status status; ao_config_get(); for (;;) { @@ -113,9 +112,7 @@ ao_igniter(void) ao_igniter_fire(igniter); ao_delay(AO_IGNITER_CHARGE_TIME); - status = ao_igniter_status(igniter); - if (status == ao_igniter_open) - ao_ignition[igniter].fired = 1; + ao_ignition[igniter].fired = 1; } } } diff --git a/src/ao_radio.c b/src/ao_radio.c index f4a9d3b2..67d5f6ba 100644 --- a/src/ao_radio.c +++ b/src/ao_radio.c @@ -432,8 +432,11 @@ ao_radio_rdf(int ms) void ao_radio_abort(void) { - ao_dma_abort(ao_radio_dma); - ao_radio_idle(); + /* Only abort if a task is waiting to receive data */ + if (RFST == RFST_SRX) { + ao_dma_abort(ao_radio_dma); + ao_radio_idle(); + } } void @@ -448,19 +451,35 @@ ao_radio_rdf_abort(void) void ao_radio_test(void) { - ao_set_monitor(0); - ao_packet_slave_stop(); - ao_radio_get(); - printf ("Hit a character to stop..."); flush(); - RFST = RFST_STX; - getchar(); - ao_radio_idle(); - ao_radio_put(); - putchar('\n'); + uint8_t mode = 2; + static __xdata radio_on; + ao_cmd_white(); + if (ao_cmd_lex_c != '\n') { + ao_cmd_decimal(); + mode = (uint8_t) ao_cmd_lex_u32; + } + mode++; + if ((mode & 2) && !radio_on) { + ao_set_monitor(0); + ao_packet_slave_stop(); + ao_radio_get(); + RFST = RFST_STX; + radio_on = 1; + } + if (mode == 3) { + printf ("Hit a character to stop..."); flush(); + getchar(); + putchar('\n'); + } + if ((mode & 1) && radio_on) { + ao_radio_idle(); + ao_radio_put(); + radio_on = 0; + } } __code struct ao_cmds ao_radio_cmds[] = { - { 'C', ao_radio_test, "C Radio carrier test" }, + { 'C', ao_radio_test, "C <1 start, 0 stop, none both> Radio carrier test" }, { 0, ao_radio_test, NULL }, }; |